huawei_modem/
pdu.rs

1//! Utilities for dealing with GSM 03.40 Protocol Data Units (PDUs).
2//!
3//! See [this Wikipedia article](https://en.wikipedia.org/wiki/GSM_03.40) for more genreal
4//! information on the format of PDUs.
5//!
6//! As of the time of writing, this library's implementation of PDUs can be classed as "passable" -
7//! in that it currently supports 2 out of the 6 specified PDU types (SMS-DELIVER and SMS-SUBMIT),
8//! and straight up refuses to parse anything else. Luckily, these two are the only ones you really
9//! need (and if they aren't, feel free to file an issue!).
10use std::fmt;
11use std::str::FromStr;
12use num::FromPrimitive;
13use std::convert::{Infallible, TryFrom};
14use crate::errors::*;
15use crate::gsm_encoding::{GsmMessageData, gsm_decode_string, decode_sms_7bit};
16
17/// Type of number value - used as part of phone numbers to indicate whether the number is
18/// international, alphanumeric, etc.
19#[repr(u8)]
20#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, FromPrimitive, Hash)]
21pub enum TypeOfNumber {
22    /// Unknown number type ('let the network handle it please').
23    Unknown = 0b0_000_0000,
24    /// International (i.e. starting with +). This is probably what you want when sending messages.
25    International = 0b0_001_0000,
26    /// National number - no prefix or suffix added.
27    National = 0b0_010_0000,
28    /// Special number - you can't send messages with this type.
29    Special = 0b0_011_0000,
30    /// Alphanumeric number - i.e. this isn't a phone number, it's actually some text that
31    /// indicates who the sender is (e.g. when banks/other companies send you SMSes).
32    ///
33    /// You can't send messages with this type.
34    Gsm = 0b0_101_0000,
35    /// Short number (not in use).
36    Short = 0b0_110_0000,
37    /// Reserved (not in use).
38    Reserved = 0b0_111_0000
39}
40/// Numbering plan idnficiation value.
41///
42/// I think this is mostly vestigial, and you'll want to set this to `IsdnTelephone`.
43#[repr(u8)]
44#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, FromPrimitive, Hash)]
45pub enum NumberingPlanIdentification {
46    NetworkDetermined = 0b0_000_0000,
47    IsdnTelephone = 0b0_000_0001,
48    Data = 0b0_000_0011,
49    Telex = 0b0_000_0100,
50    National = 0b0_000_1000,
51    Private = 0b0_000_1001,
52    Ermes = 0b0_000_1010
53}
54/// Address type, comprised of a `TypeOfNumber` and `NumberingPlanIdentification` value.
55///
56/// It is **highly** recommended that you just use the `Default` value of this `struct`, unless you
57/// know what you're doing, and that your phone numbers are in international format (i.e. starting
58/// with `+`). 
59#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
60pub struct AddressType {
61    pub type_of_number: TypeOfNumber,
62    pub numbering_plan_identification: NumberingPlanIdentification
63}
64impl Default for AddressType {
65    fn default() -> Self {
66        AddressType {
67            type_of_number: TypeOfNumber::International,
68            numbering_plan_identification: NumberingPlanIdentification::IsdnTelephone
69        }
70    }
71}
72impl TryFrom<u8> for AddressType {
73    type Error = HuaweiError;
74    fn try_from(b: u8) -> HuaweiResult<Self>  {
75        let ton = b & 0b0_111_0000;
76        let ton = TypeOfNumber::from_u8(ton)
77            .ok_or(HuaweiError::InvalidPdu("invalid type_of_number"))?;
78        let npi = b & 0b0_000_1111;
79        let npi = NumberingPlanIdentification::from_u8(npi)
80            .ok_or(HuaweiError::InvalidPdu("invalid numbering_plan_identification"))?;
81        Ok(Self {
82            type_of_number: ton,
83            numbering_plan_identification: npi
84        })
85    }
86}
87impl Into<u8> for AddressType {
88    fn into(self) -> u8 {
89        let mut ret: u8 = 0b1_000_0000;
90        ret |= self.type_of_number as u8;
91        ret |= self.numbering_plan_identification as u8;
92        ret
93    }
94}
95/// A GSM phone number, encoded using their weird half-octet encoding.
96#[derive(Debug, Clone, PartialEq, Eq, Hash)]
97pub struct PhoneNumber(pub Vec<u8>);
98/// Make a phone number from some ordinary bytes.
99/// 
100/// Note that **only the least significant 4 bytes** are used in this conversion, due to the way
101/// GSM phone numbers work. The top 4 bytes are discarded!
102impl<'a> From<&'a [u8]> for PhoneNumber {
103    fn from(b: &[u8]) -> Self {
104        let mut ret = vec![];
105        for b in b.iter() {
106            let first = b & 0b0000_1111;
107            let second = (b & 0b1111_0000) >> 4;
108            ret.push(first);
109            if second != 0b0000_1111 {
110                ret.push(second);
111            }
112        }
113        PhoneNumber(ret)
114    }
115}
116impl PhoneNumber {
117    /// Make a `PhoneNumber` for an alphanumeric GSM sender address.
118    pub fn from_gsm(b: &[u8], len: usize) -> Self {
119        PhoneNumber(decode_sms_7bit(b, 0, len))
120    }
121    /// Represent this phone number as normal bytes, instead of their weird as hell encoding.
122    pub fn as_bytes(&self) -> Vec<u8> {
123        let mut ret = vec![];
124        let mut cur = 0b0000_0000;
125        for (i, b) in self.0.iter().enumerate() {
126            let mut b = *b;
127            if i % 2 == 0 {
128                cur |= b;
129            }
130            else {
131                b = b << 4;
132                cur |= b;
133                ret.push(cur);
134                cur = 0b0000_0000;
135            }
136        }
137        if self.0.len() % 2 != 0 {
138            cur |= 0b1111_0000;
139            ret.push(cur);
140        }
141        ret
142    }
143}
144/// A PDU address (i.e. phone number, and number type). This is what you want to use for
145/// representing phone numbers, most likely.
146///
147/// Use the `FromStr` implementation here to convert regular string phone numbers into weird PDU
148/// format. Note that alphanumeric numbers are not supported at this time (only normal phone
149/// numbers).
150#[derive(Debug, Clone, PartialEq, Eq, Hash)]
151pub struct PduAddress {
152    pub type_addr: AddressType,
153    pub number: PhoneNumber
154}
155impl fmt::Display for PduAddress {
156    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157
158        let prefix = match self.type_addr.type_of_number {
159            TypeOfNumber::International => "+",
160            _ => ""
161        };
162        write!(f, "{}", prefix)?;
163        if self.type_addr.type_of_number == TypeOfNumber::Gsm {
164            write!(f, "{}", gsm_decode_string(&self.number.0))?;
165        }
166        else {
167            for b in self.number.0.iter() {
168                write!(f, "{}", b)?;
169            }
170        }
171        Ok(())
172    }
173}
174impl FromStr for PduAddress {
175    type Err = Infallible;
176    fn from_str(st: &str) -> Result<Self, Infallible> {
177        let mut int = false;
178        let buf = st.chars()
179            .filter_map(|x| {
180                match x {
181                    '0'...'9' => Some(x as u8 - 48),
182                    '+' => {
183                        int = true;
184                        None
185                    },
186                    _ => None
187                }
188            }).collect::<Vec<_>>();
189        let ton = if int {
190            TypeOfNumber::International
191        }
192        else {
193            TypeOfNumber::Unknown
194        };
195        Ok(PduAddress {
196            type_addr: AddressType {
197                type_of_number: ton,
198                numbering_plan_identification: NumberingPlanIdentification::IsdnTelephone
199            },
200            number: PhoneNumber(buf)
201        })
202    }
203}
204impl<'a> TryFrom<&'a [u8]> for PduAddress {
205    type Error = HuaweiError;
206    fn try_from(b: &[u8]) -> HuaweiResult<Self> {
207        if b.len() < 3 {
208            Err(HuaweiError::InvalidPdu("tried to make a PduAddress from less than 3 bytes"))?
209        }
210        let len = b[0] as usize;
211        let type_addr = AddressType::try_from(b[1])?;
212        let number = if type_addr.type_of_number == TypeOfNumber::Gsm {
213            let len = (len * 4) / 7;
214            PhoneNumber::from_gsm(&b[2..], len)
215        }
216        else {
217            PhoneNumber::from(&b[2..])
218        };
219        Ok(PduAddress { type_addr, number })
220    }
221}
222impl PduAddress {
223    /// Convert this address into bytes, as represented in the actual PDU.
224    ///
225    /// The `broken_len` flag controls whether to represent the length as the length in bytes of
226    /// the whole PduAddress (false), or just the length of the phone number contained within (true).
227    ///
228    /// In testing, it seems as if it should pretty much always be `true`, which is weird. A future
229    /// version of the crate may well just remove the parameter and default to true.
230    pub fn as_bytes(&self, broken_len: bool) -> Vec<u8> {
231        let mut ret = vec![];
232        ret.push(self.type_addr.into());
233        ret.extend(self.number.as_bytes());
234        let len = if broken_len {
235            self.number.0.len()
236        } else {
237            ret.len()
238        };
239        ret.insert(0, len as u8);
240        ret
241    }
242}
243/// SMS PDU message type.
244#[repr(u8)]
245#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, FromPrimitive)]
246pub enum MessageType {
247    /// SMS-DELIVER (SC to MT) or SMS-DELIVER-REPORT (MT to SC)
248    SmsDeliver = 0b000000_00,
249    /// SMS-STATUS-REPORT (SC to MT) or SMS-COMMAND (MT to SC)
250    SmsCommand = 0b000000_10,
251    /// SMS-SUBMIT-REPORT (SC to MT) or SMS-SUBMIT (MT to SC)
252    SmsSubmit = 0b000000_01,
253    /// Reserved for future use.
254    Reserved = 0b000000_11
255}
256/// Validity of the VP field.
257#[repr(u8)]
258#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, FromPrimitive)]
259pub enum VpFieldValidity {
260    /// Invalid.
261    Invalid = 0b0000_00_00,
262    /// Valid, in relative format.
263    Relative = 0b0000_10_00,
264    /// Valid, in enhanced format.
265    Enhanced = 0b0000_01_00,
266    /// Valid, in absolute format.
267    Absolute = 0b0000_11_00,
268}
269/// The first octet of a SMS-SUBMIT PDU.
270#[derive(Debug, Copy, Clone, PartialEq, Eq)]
271pub struct PduFirstOctet {
272    /// Message type.
273    mti: MessageType,
274    /// Reject duplicates (?).
275    rd: bool,
276    /// Validity and format of the VP field.
277    vpf: VpFieldValidity,
278    /// Whether to request a status report when the message is sent succesfully.
279    srr: bool,
280    /// Does the user data segment contain a data header?
281    udhi: bool,
282    /// Do replies to this message use the same settings as this message?
283    rp: bool
284}
285impl From<u8> for PduFirstOctet {
286    fn from(b: u8) -> Self {
287        let rd = (b & 0b00000100) > 0;
288        let srr = (b & 0b00100000) > 0;
289        let udhi = (b & 0b01000000) > 0;
290        let rp = (b & 0b10000000) > 0;
291        let mti = MessageType::from_u8(b & 0b000000_11)
292            .expect("MessageType conversions should be exhaustive!");
293        let vpf = VpFieldValidity::from_u8(b & 0b0000_11_00)
294            .expect("VpFieldValidity conversions should be exhaustive!");
295        PduFirstOctet { rd, srr, udhi, rp, mti, vpf }
296    }
297}
298impl Into<u8> for PduFirstOctet {
299    fn into(self) -> u8 {
300        let mut ret = 0b0000_0000;
301        ret |= self.mti as u8;
302        ret |= self.vpf as u8;
303        if self.rd {
304            ret |= 0b00000100;
305        }
306        if self.srr {
307            ret |= 0b00100000;
308        }
309        if self.udhi {
310            ret |= 0b01000000;
311        }
312        if self.rp {
313            ret |= 0b10000000;
314        }
315        ret
316    }
317}
318/// The data coding scheme of the message.
319///
320/// Basically, this `enum` is a decoded 8-bit field that has a bunch of different cases, which is
321/// why there are so many options here.
322/// 
323/// The meanings explained in the Huawei spec are very confusing and sometimes overlapping.
324/// Use the `encoding` method to figure out what encoding to use, which is probably the only real
325/// use you're going to have for this `struct` anyway.
326#[derive(Debug, Copy, Clone, PartialEq, Eq)]
327pub enum DataCodingScheme {
328    /// Standard coding scheme.
329    Standard {
330        /// Whether or not the message is compressed, but this isn't actually supported.
331        compressed: bool,
332        /// The message class (flash SMS, stored to SIM only, etc.)
333        class: MessageClass,
334        /// The message encoding (7-bit, UCS-2, 8-bit)
335        encoding: MessageEncoding
336    },
337    /// Reserved value.
338    Reserved,
339    /// Discard the message content, but display the message waiting indication to the user.
340    MessageWaitingDiscard {
341        /// Enables or disables message waiting indication.
342        waiting: bool,
343        /// Type of message waiting.
344        type_indication: MessageWaitingType,
345    },
346    /// Store the message content, and display the message waiting indication to the user.
347    MessageWaiting {
348        /// Enables or disables message waiting indication.
349        waiting: bool,
350        /// Type of message waiting.
351        type_indication: MessageWaitingType,
352        /// Whether or not the message is encoded in UCS-2 format.
353        ucs2: bool
354    }
355}
356impl DataCodingScheme {
357    /// Determine which character encoding the message uses (i.e. GSM 7-bit, UCS-2, ...)
358    ///
359    /// (Some of these answers might be guesses.)
360    pub fn encoding(&self) -> MessageEncoding {
361        use self::DataCodingScheme::*;
362        match *self {
363            Standard { encoding, .. } => encoding,
364            Reserved => MessageEncoding::Gsm7Bit,
365            MessageWaitingDiscard { .. } => MessageEncoding::Gsm7Bit,
366            MessageWaiting { ucs2, .. } => if ucs2 {
367                MessageEncoding::Ucs2
368            }
369            else {
370                MessageEncoding::Gsm7Bit
371            }
372        }
373    }
374}
375impl From<u8> for DataCodingScheme {
376    fn from(b: u8) -> Self {
377        if (b & 0b1100_0000) == 0b0000_0000 {
378            let compressed = (b & 0b0010_0000) > 0;
379            let reserved = (b & 0b0001_0000) > 0;
380            let class = if reserved {
381                // XXX: No default is actually specified in the Huawei spec; I just chose
382                // StoreToNv, because that's what we send by default.
383                MessageClass::StoreToNv
384            }
385            else {
386                MessageClass::from_u8(b & 0b0000_0011)
387                    .expect("MessageClass conversions should be exhaustive!")
388            };
389            let encoding = MessageEncoding::from_u8(b & 0b0000_1100)
390                .expect("MessageEncoding conversions should be exhaustive!");
391            DataCodingScheme::Standard { compressed, class, encoding }
392        }
393        else if (b & 0b1111_0000) == 0b1111_0000 {
394            let compressed = false;
395            let class = MessageClass::from_u8(b & 0b0000_0011)
396                .expect("MessageClass conversions should be exhaustive!");
397            let encoding = if (b & 0b0000_0100) > 0 {
398                MessageEncoding::Gsm7Bit
399            }
400            else {
401                MessageEncoding::EightBit
402            };
403            DataCodingScheme::Standard { compressed, class, encoding }
404        }
405        else if (b & 0b1111_0000) == 0b1100_0000 {
406            let waiting = (b & 0b0000_1000) > 0;
407            let type_indication = MessageWaitingType::from_u8(b & 0b0000_0011)
408                .expect("MessageWaitingType conversions should be exhaustive!");
409            DataCodingScheme::MessageWaitingDiscard { waiting, type_indication }
410        }
411        else if (b & 0b1111_0000) == 0b1101_0000 || (b & 0b1111_0000) == 0b1110_0000 {
412            let ucs2 = (b & 0b1111_0000) == 0b1110_0000;
413            let waiting = (b & 0b0000_1000) > 0;
414            let type_indication = MessageWaitingType::from_u8(b & 0b0000_0011)
415                .expect("MessageWaitingType conversions should be exhaustive!");
416            DataCodingScheme::MessageWaiting { ucs2, waiting, type_indication }
417        }
418        else {
419            DataCodingScheme::Reserved
420        }
421    }
422}
423impl Into<u8> for DataCodingScheme {
424    fn into(self) -> u8 {
425        use self::DataCodingScheme::*;
426        match self {
427            Standard { compressed, class, encoding } => {
428                let mut ret = 0b0001_0000;
429                if compressed {
430                    ret |= 0b0010_0000;
431                }
432                ret |= class as u8;
433                ret |= encoding as u8;
434                ret
435            },
436            Reserved => 0b0100_0101,
437            MessageWaiting { waiting, type_indication, ucs2 } => {
438                let mut ret = if ucs2 {
439                    0b1110_0000
440                }
441                else {
442                    0b1101_0000
443                };
444                if waiting {
445                    ret |= 0b0000_1000;
446                }
447                ret |= type_indication as u8;
448                ret
449            },
450            MessageWaitingDiscard { waiting, type_indication } => {
451                let mut ret = 0b1100_0000;
452                if waiting {
453                    ret |= 0b0000_1000;
454                }
455                ret |= type_indication as u8;
456                ret
457            }
458        }
459    }
460}
461/// Type of message waiting.
462#[repr(u8)]
463#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, FromPrimitive)]
464pub enum MessageWaitingType {
465    Voice = 0b000000_00,
466    Fax = 0b000000_01,
467    Email = 0b000000_10,
468    Unknown = 0b000000_11
469}
470/// Class of message received.
471#[repr(u8)]
472#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, FromPrimitive)]
473pub enum MessageClass {
474    /// Silent (class 0)  - display on the phone's UI, but don't store in memory.
475    Silent = 0b000000_00,
476    /// Store to the NV (class 1), or SIM card if the NV is full.
477    StoreToNv = 0b000000_01,
478    /// Store to the SIM card only (class 2).
479    StoreToSim = 0b000000_10,
480    /// Store to the TE (class 3).
481    StoreToTe = 0b000000_11
482}
483/// SMS message data encoding.
484#[repr(u8)]
485#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, FromPrimitive)]
486pub enum MessageEncoding {
487    /// GSM packed 7-bit encoding.
488    Gsm7Bit = 0b0000_00_00,
489    /// Binary 8-bit encoding.
490    EightBit = 0b0000_01_00,
491    /// UCS-2 (i.e. UTF-16) encoding.
492    Ucs2 = 0b0000_10_00,
493    /// Reserved for future use.
494    Reserved = 0b0000_11_00,
495}
496/// The first octet of a SMS-DELIVER PDU.
497#[derive(Debug, Clone, PartialEq, Eq)]
498pub struct DeliverPduFirstOctet {
499    /// Message type.
500    mti: MessageType,
501    /// Indicates whetehr a status report was requested.
502    sri: bool,
503    /// Does the user data segment contain a data header?
504    udhi: bool,
505    /// Do replies to this message use the same settings as this message?
506    rp: bool
507}
508impl From<u8> for DeliverPduFirstOctet {
509    fn from(b: u8) -> Self {
510        let mti = MessageType::from_u8(b & 0b000000_11)
511            .expect("MessageType conversions should be exhaustive!");
512        let sri = (b & 0b00100000) > 0;
513        let udhi = (b & 0b01000000) > 0;
514        let rp = (b & 0b01000000) > 0;
515        DeliverPduFirstOctet { mti, sri, udhi, rp }
516    }
517}
518/// Service centre timestamp.
519#[derive(Debug, Clone, PartialEq, Eq)]
520pub struct SmscTimestamp {
521    year: u8,
522    month: u8,
523    day: u8,
524    hour: u8,
525    minute: u8,
526    second: u8,
527    /// Hours' difference between local time and GMT.
528    timezone: u8
529}
530pub(crate) fn reverse_byte(b: u8) -> u8 {
531    let units = b >> 4;
532    let tens = b & 0b0000_1111;
533    (tens * 10) + units
534}
535impl<'a> TryFrom<&'a [u8]> for SmscTimestamp {
536    type Error = HuaweiError;
537    fn try_from(b: &[u8]) -> HuaweiResult<Self> {
538        if b.len() != 7 {
539            Err(HuaweiError::InvalidPdu("SmscTimestamp must be 7 bytes long"))?
540        }
541        Ok(SmscTimestamp {
542            year: reverse_byte(b[0]),
543            month: reverse_byte(b[1]),
544            day: reverse_byte(b[2]),
545            hour: reverse_byte(b[3]),
546            minute: reverse_byte(b[4]),
547            second: reverse_byte(b[5]),
548            timezone: reverse_byte(b[6]),
549        })
550    }
551}
552/// An SMS-DELIVER PDU.
553///
554/// **NB:** For simple usage, you'll only need to care about the `originating_address` field and
555/// the `get_message_data` method!
556#[derive(Debug, Clone, PartialEq, Eq)]
557pub struct DeliverPdu {
558    /// Service centre address, if provided here.
559    pub sca: Option<PduAddress>,
560    /// First octet (contains some extra fields).
561    pub first_octet: DeliverPduFirstOctet,
562    /// Originating address (i.e. message sender).
563    pub originating_address: PduAddress,
564    /// Message data coding scheme.
565    pub dcs: DataCodingScheme,
566    /// Message timestamp, from the service centre.
567    pub scts: SmscTimestamp,
568    /// User data.
569    pub user_data: Vec<u8>,
570    /// User data length.
571    pub user_data_len: u8
572}
573impl DeliverPdu {
574    /// Get the actual data (i.e. text or binary content) of the message.
575    ///
576    /// Methods on `GsmMessageData` let you convert this into actual text.
577    pub fn get_message_data(&self) -> GsmMessageData {
578        GsmMessageData {
579            bytes: self.user_data.clone(),
580            user_data_len: self.user_data_len,
581            encoding: self.dcs.encoding(),
582            udh: self.first_octet.udhi
583        }
584    }
585}
586impl<'a> TryFrom<&'a [u8]> for DeliverPdu {
587    type Error = HuaweiError;
588    fn try_from(b: &[u8]) -> HuaweiResult<Self> {
589        if b.len() == 0 {
590            return Err(HuaweiError::InvalidPdu("zero-length input"));
591        }
592        let scalen = b[0];
593        let mut offset: usize = scalen as usize + 1;
594        let sca = if scalen > 0 {
595            let o = offset - 1;
596            check_offset!(b, o, "SCA");
597            Some(PduAddress::try_from(&b[0..offset])?)
598        }
599        else {
600            None
601        };
602        check_offset!(b, offset, "first octet");
603        let first_octet = DeliverPduFirstOctet::from(b[offset]);
604        offset += 1;
605        check_offset!(b, offset, "originating address len");
606        let destination_len_nybbles = b[offset];
607        // destination_len_nybbles represents the length of the address, in nybbles (half-octets).
608        // Therefore, we divide by 2, rounding up, to get the number of full octets.
609        let destination_len_octets = (destination_len_nybbles / 2) + destination_len_nybbles % 2;
610        // destination_offset = what we're going to add to the offset to get the new offset
611        // This is the destination length, in octets, plus one byte for the address length field,
612        // and another because range syntax is non-inclusive.
613        let destination_offset = (destination_len_octets as usize) + 2;
614        let destination_end = offset + destination_offset;
615        let de = destination_end - 1;
616        check_offset!(b, de, "originating address");
617        let originating_address = PduAddress::try_from(&b[offset..destination_end])?;
618        offset += destination_offset;
619        check_offset!(b, offset, "protocol identifier");
620        let _pid = b[offset];
621        offset += 1;
622        check_offset!(b, offset, "data coding scheme");
623        let dcs = DataCodingScheme::from(b[offset]);
624        offset += 1;
625        let scts_end = offset + 7;
626        let ss = offset + 6;
627        check_offset!(b, ss, "service center timestamp");
628        let scts = SmscTimestamp::try_from(&b[offset..scts_end])?;
629        offset += 7;
630        check_offset!(b, offset, "user data len");
631        let user_data_len = b[offset];
632        offset += 1;
633        let user_data = if b.get(offset).is_some() {
634            b[offset..].to_owned()
635        }
636        else {
637            vec![]
638        };
639        Ok(DeliverPdu {
640            sca,
641            first_octet,
642            originating_address,
643            dcs,
644            scts,
645            user_data,
646            user_data_len
647        })
648
649    }
650}
651/// An SMS-SUBMIT PDU.
652///
653/// **NB:** For simple usage, ignore 99% of the stuff in this module and just use
654/// `Pdu::make_simple_message`!
655#[derive(Debug, Clone, PartialEq, Eq)]
656pub struct Pdu {
657    /// Service centre address, if provided here.
658    ///
659    /// If you haven't set the service center address for all messages (see the `set_smsc_addr`
660    /// function in `cmd::sms`), you'll need to provide it in SMS-SUBMIT PDUs using the `set_sca`
661    /// method.
662    pub sca: Option<PduAddress>,
663    /// First octet (contains some extra fields).
664    pub first_octet: PduFirstOctet,
665    /// Message ID.
666    ///
667    /// This is set to 0 in `make_simple_message`. Presumably, you might ostensibly be able to use
668    /// it to store outgoing messages in modem memory and then address them later?
669    pub message_id: u8,
670    /// Destination address (i.e. mesage recipient).
671    pub destination: PduAddress,
672    /// Message data coding scheme.
673    pub dcs: DataCodingScheme,
674    /// Validity period (used for message expiry).
675    ///
676    /// FIXME: as yet undocumented.
677    pub validity_period: u8,
678    /// User data.
679    pub user_data: Vec<u8>,
680    /// User data length.
681    pub user_data_len: u8
682}
683impl Pdu {
684    /// Set the SMS service centre address.
685    pub fn set_sca(&mut self, sca: PduAddress) {
686        self.sca = Some(sca);
687    }
688    /// Simple helper function to send a message to someone, prefilling in all the annoying fields
689    /// for you.
690    pub fn make_simple_message(recipient: PduAddress, msg: GsmMessageData) -> Self {
691        Pdu {
692            sca: None,
693            first_octet: PduFirstOctet {
694                mti: MessageType::SmsSubmit,
695                rd: false,
696                vpf: VpFieldValidity::Invalid,
697                rp: false,
698                udhi: msg.udh,
699                srr: false
700            },
701            message_id: 0,
702            destination: recipient,
703            dcs: DataCodingScheme::Standard {
704                compressed: false,
705                class: MessageClass::StoreToNv,
706                encoding: msg.encoding
707            },
708            validity_period: 0,
709            user_data: msg.bytes,
710            user_data_len: msg.user_data_len as u8
711        }
712    }
713}
714impl Pdu {
715    /// Convert to wire-format bytes, with a TPDU length value.
716    pub fn as_bytes(&self) -> (Vec<u8>, usize) {
717        let mut ret = vec![];
718        let mut scalen = 1;
719        if let Some(ref sca) = self.sca {
720            let sca = sca.as_bytes(false);
721            scalen = sca.len();
722            ret.extend(sca);
723        }
724        else {
725            ret.push(0);
726        }
727        ret.push(self.first_octet.into());
728        ret.push(self.message_id);
729        ret.extend(self.destination.as_bytes(true));
730        ret.push(0);
731        ret.push(self.dcs.into());
732        if self.first_octet.vpf != VpFieldValidity::Invalid {
733            ret.push(self.validity_period);
734        }
735        ret.push(self.user_data_len);
736        ret.extend(self.user_data.clone());
737        let tpdu_len = ret.len() - scalen;
738        (ret, tpdu_len)
739    }
740}
741pub(crate) struct HexData<'a>(pub &'a [u8]);
742impl<'a> fmt::Display for HexData<'a> {
743    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
744       for b in self.0.iter() {
745           write!(f, "{:02X}", b)?;
746       }
747       Ok(())
748    }
749}
750impl<'a> HexData<'a> {
751    pub fn decode(data: &str) -> HuaweiResult<Vec<u8>> {
752        data.as_bytes()
753            .chunks(2)
754            .map(::std::str::from_utf8)
755            .map(|x| {
756                match x {
757                    Ok(x) => u8::from_str_radix(x, 16)
758                        .map_err(|_| HuaweiError::InvalidPdu("invalid hex string")),
759                    Err(_) => Err(HuaweiError::InvalidPdu("invalid hex string"))
760                }
761            })
762            .collect()
763    }
764}