sbd_lib/
information_element.rs

1use crate::mo::LocationInformation;
2use crate::{mo, mt, Result};
3use byteorder;
4use std::io::Cursor;
5use std::{fmt, io::Read, io::Write};
6
7const PROTOCOL_REVISION_NUMBER: u8 = 1;
8
9pub trait SbdHeader: fmt::Debug {
10    //fn read_from(read: &Read) -> Result<Box<Self>>;
11    fn write_to(&self, write: &mut dyn Write) -> Result<()>;
12    fn imei(&self) -> &str;
13    fn len(&self) -> usize;
14    fn as_mo(&self) -> Option<&mo::Header> {
15        None
16    }
17    fn as_mt(&self) -> Option<&mt::Header> {
18        None
19    }
20}
21
22/// A mobile-originated or mobile-terminated header
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub enum Header {
25    /// Information element holding the mobile-originated header.
26    MOHeader(mo::Header),
27    /// Information element holding the mobile-terminated header.
28    MTHeader(mt::Header),
29}
30
31impl fmt::Display for Header {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        match self {
34            Header::MOHeader(header) => write!(
35                f,
36                "MO auto_id: {}, session_status: {:?}, imei: {},momsn: {}, mtmsn: {},  time: {}",
37                header.auto_id,
38                header.session_status,
39                header.imei(),
40                header.momsn,
41                header.mtmsn,
42                header.time_of_session
43            ),
44            Header::MTHeader(header) => write!(
45                f,
46                "MT message_id: {}, imei: {}, flags: {}",
47                header.message_id,
48                header.imei(),
49                header.flags
50            ),
51        }
52    }
53}
54
55impl SbdHeader for Header {
56    fn write_to(&self, write: &mut dyn Write) -> Result<()> {
57        match self {
58            Header::MOHeader(header) => header.write_to(write)?,
59            Header::MTHeader(header) => header.write_to(write)?,
60        }
61        Ok(())
62    }
63
64    fn imei(&self) -> &str {
65        match self {
66            Header::MOHeader(header) => header.imei(),
67            Header::MTHeader(header) => header.imei(),
68        }
69    }
70
71    fn len(&self) -> usize {
72        match *self {
73            Header::MOHeader(ref header) => header.len(),
74            Header::MTHeader(ref header) => header.len(),
75        }
76    }
77    fn as_mo(&self) -> Option<&mo::Header> {
78        if let Header::MOHeader(ref header) = *self {
79            Some(header)
80        } else {
81            None
82        }
83    }
84
85    fn as_mt(&self) -> Option<&mt::Header> {
86        if let Header::MTHeader(ref header) = *self {
87            Some(header)
88        } else {
89            None
90        }
91    }
92}
93
94impl From<mo::Header> for Header {
95    fn from(header: mo::Header) -> Self {
96        Header::MOHeader(header)
97    }
98}
99
100impl From<mt::Header> for Header {
101    fn from(header: mt::Header) -> Self {
102        Header::MTHeader(header)
103    }
104}
105
106/// A mobile-originated or mobile-terminated status.
107#[derive(Clone, Debug, PartialEq, Eq)]
108pub enum Status {
109    /// Information element holding the mobile-originated status.
110    MOStatus(mo::ConfirmationStatus),
111    /// Information element holding the mobile-terminated status.
112    MTStatus(mt::ConfirmationStatus),
113}
114
115impl Status {
116    fn write_to<W: Write>(&self, write: &mut W) -> Result<()> {
117        match self {
118            Status::MOStatus(status) => status.write_to(write)?,
119            Status::MTStatus(status) => status.write_to(write)?,
120        };
121        Ok(())
122    }
123
124    pub fn len(&self) -> usize {
125        match self {
126            Status::MOStatus(status) => status.len(),
127            Status::MTStatus(status) => status.len(),
128        }
129    }
130}
131
132impl From<mo::ConfirmationStatus> for Status {
133    fn from(status: mo::ConfirmationStatus) -> Self {
134        Status::MOStatus(status)
135    }
136}
137
138impl From<mt::ConfirmationStatus> for Status {
139    fn from(status: mt::ConfirmationStatus) -> Self {
140        Status::MTStatus(status)
141    }
142}
143
144/// A information element, or IE.
145///
146/// These are the building blocks of a SBD message.
147#[derive(Clone, Debug, PartialEq, Eq)]
148pub enum InformationElement {
149    /// Information element holding the header MO or MT.
150    Header(Header),
151    /// The mobile originated payload.
152    MOPayload(Vec<u8>),
153    /// The mobile originated payload.
154    MTPayload(Vec<u8>),
155    /// Message Delivery Confirmation
156    Status(Status),
157    /// The mobile originated location information.
158    LocationInformation(LocationInformation),
159}
160
161impl InformationElement {
162    /// Reads this information element from a `Read`.
163    pub fn read_single<R: Read>(mut read: R) -> Result<Self> {
164        use crate::Error;
165        use byteorder::{BigEndian, ReadBytesExt};
166
167        let iei = read.read_u8().map_err(Error::Io)?;
168        let length = read.read_u16::<BigEndian>().map_err(Error::Io)?;
169        match iei {
170            0x1 => Ok(mo::Header::read_from(&mut read)?.into()),
171            0x41 => Ok(mt::Header::read_from(&mut read)?.into()),
172            0x2 | 0x42 => {
173                let mut payload = vec![0; length as usize];
174                read.read_exact(&mut payload).map_err(Error::Io)?;
175                Ok(if iei == 0x2 {
176                    InformationElement::MOPayload(payload)
177                } else {
178                    InformationElement::MTPayload(payload)
179                })
180            }
181            0x3 => {
182                let mut location = vec![0; length as usize];
183                read.read_exact(&mut location).map_err(Error::Io)?;
184                let mut cur = Cursor::new(&location);
185                let flags = cur.read_u8()?;
186                let latitude = (cur.read_u8()?, cur.read_u16::<BigEndian>()?);
187                let longitude = (cur.read_u8()?, cur.read_u16::<BigEndian>()?);
188
189                let radius = if length == 11 {
190                    Some(cur.read_u32::<BigEndian>()?)
191                } else {
192                    None
193                };
194
195                let loc = LocationInformation::new(flags, latitude, longitude, radius);
196
197                Ok(InformationElement::LocationInformation(loc))
198            }
199            0x44 => Ok(mt::ConfirmationStatus::read_from(&mut read)?.into()),
200            0x5 => Ok(mo::ConfirmationStatus::read_from(&mut read)?.into()),
201
202            _ => Err(Error::InvalidInformationElementIdentifier(iei)),
203        }
204    }
205
206    pub fn parse<R: Read>(mut read: R) -> Result<Vec<Self>> {
207        use crate::Error;
208        use byteorder::{BigEndian, ReadBytesExt};
209
210        let protocol_revision_number = read.read_u8()?;
211        if protocol_revision_number != PROTOCOL_REVISION_NUMBER {
212            return Err(Error::InvalidProtocolRevisionNumber(
213                protocol_revision_number,
214            ));
215        }
216        let overall_message_length = read.read_u16::<BigEndian>()?;
217        let mut message = vec![0; overall_message_length as usize];
218        read.read_exact(&mut message)?;
219
220        let mut cursor = Cursor::new(message);
221        let mut information_elements = Vec::new();
222        while cursor.position() < u64::from(overall_message_length) {
223            information_elements.push(InformationElement::read_single(&mut cursor)?);
224        }
225        Ok(information_elements)
226    }
227
228    /// Returns the length of this information element, including the information element header.
229    pub fn len(&self) -> usize {
230        match self {
231            InformationElement::Header(h) => h.len(),
232            InformationElement::Status(status) => status.len(),
233            InformationElement::MOPayload(payload) | InformationElement::MTPayload(payload) => {
234                payload.len() + 3
235            }
236            InformationElement::LocationInformation(location) => location.len(),
237        }
238    }
239
240    /// Returns true if this information element is empty.
241    ///
242    /// At this point, only can be true if the payload is empty.
243    pub fn is_empty(&self) -> bool {
244        match *self {
245            InformationElement::MOPayload(ref payload) => payload.is_empty(),
246            _ => false,
247        }
248    }
249
250    /// Writes this information element to a `Write`.
251    pub fn write_to<W: Write>(&self, mut write: &mut W) -> Result<()> {
252        use crate::Error;
253        use byteorder::{BigEndian, WriteBytesExt};
254
255        match self {
256            InformationElement::Header(header) => {
257                header.write_to(&mut write)?;
258            }
259            InformationElement::Status(status) => {
260                status.write_to(&mut write)?;
261            }
262            InformationElement::MOPayload(payload) => {
263                write.write_u8(2)?;
264                let len = payload.len();
265                if len > u16::MAX as usize {
266                    return Err(Error::PayloadTooLong(len));
267                } else {
268                    write.write_u16::<BigEndian>(len as u16)?;
269                }
270                write.write_all(payload)?;
271            }
272            InformationElement::MTPayload(payload) => {
273                write.write_u8(0x42)?;
274                let len = payload.len();
275                if len > u16::MAX as usize {
276                    return Err(Error::PayloadTooLong(len));
277                } else {
278                    write.write_u16::<BigEndian>(len as u16)?;
279                }
280                write.write_all(payload)?;
281            }
282            InformationElement::LocationInformation(location) => {
283                location.write_to(&mut write)?;
284            }
285        }
286        Ok(())
287    }
288}
289
290impl From<mo::ConfirmationStatus> for InformationElement {
291    fn from(status: mo::ConfirmationStatus) -> Self {
292        InformationElement::Status(Status::from(status))
293    }
294}
295
296impl From<mt::ConfirmationStatus> for InformationElement {
297    fn from(status: mt::ConfirmationStatus) -> Self {
298        InformationElement::Status(Status::from(status))
299    }
300}
301
302impl From<mo::Header> for InformationElement {
303    fn from(header: mo::Header) -> Self {
304        InformationElement::Header(Header::from(header))
305    }
306}
307
308impl From<mt::Header> for InformationElement {
309    fn from(header: mt::Header) -> Self {
310        InformationElement::Header(Header::from(header))
311    }
312}
313
314impl From<mo::LocationInformation> for InformationElement {
315    fn from(location: mo::LocationInformation) -> Self {
316        InformationElement::LocationInformation(location)
317    }
318}
319
320#[cfg(test)]
321mod tests {
322    use super::*;
323    use chrono::{TimeZone, Utc};
324    use std::fs::File;
325    use std::io::{Cursor, Read, Seek, SeekFrom};
326
327    #[test]
328    fn read_from() {
329        let mut file = File::open("data/0-mo.sbd").unwrap();
330        file.seek(SeekFrom::Start(3)).unwrap();
331        {
332            let read = Read::by_ref(&mut file).take(31);
333            match InformationElement::read_single(read).unwrap() {
334                InformationElement::Header(header) => {
335                    if let Some(header) = header.as_mo() {
336                        assert_eq!(1894516585, header.auto_id);
337                        assert_eq!(b"300234063904190", &header.imei);
338                        assert_eq!(mo::SessionStatus::Ok, header.session_status);
339                        assert_eq!(75, header.momsn);
340                        assert_eq!(0, header.mtmsn);
341                        assert_eq!(
342                            Utc.ymd(2015, 7, 9).and_hms(18, 15, 08),
343                            header.time_of_session
344                        );
345                    } else {
346                        panic!("Unexpected information element")
347                    }
348                }
349                _ => panic!("Unexpected information element"),
350            }
351        }
352        match InformationElement::read_single(file).unwrap() {
353            InformationElement::MOPayload(data) => {
354                assert_eq!(b"test message from pete", data.as_slice())
355            }
356            _ => panic!("Unexpected information element"),
357        }
358    }
359
360    #[test]
361    fn undersized() {
362        let mut file = File::open("data/0-mo.sbd").unwrap();
363        file.seek(SeekFrom::Start(3)).unwrap();
364        let read = file.take(30);
365        assert!(InformationElement::read_single(read).is_err());
366    }
367
368    #[test]
369    fn header_len() {
370        let header = mo::Header {
371            auto_id: 1,
372            imei: [0; 15],
373            session_status: mo::SessionStatus::Ok,
374            momsn: 1,
375            mtmsn: 1,
376            time_of_session: Utc.ymd(2017, 10, 17).and_hms(12, 0, 0),
377        };
378        let ie = InformationElement::Header(header.into());
379        assert_eq!(31, ie.len());
380    }
381
382    #[test]
383    fn payload_len() {
384        assert_eq!(4, InformationElement::MOPayload(vec![1]).len());
385    }
386
387    #[test]
388    fn location_information_len() {
389        assert_eq!(
390            10,
391            InformationElement::LocationInformation(LocationInformation::new(
392                0,
393                (60, 1111),
394                (60, 1111),
395                None
396            ))
397            .len()
398        );
399
400        assert_eq!(
401            14,
402            InformationElement::LocationInformation(LocationInformation::new(
403                0,
404                (60, 1111),
405                (60, 1111),
406                Some(5)
407            ))
408            .len()
409        );
410    }
411
412    #[test]
413    fn roundtrip_header() {
414        let header = mo::Header {
415            auto_id: 1,
416            imei: [0; 15],
417            session_status: mo::SessionStatus::Ok,
418            momsn: 1,
419            mtmsn: 1,
420            time_of_session: Utc.ymd(2017, 10, 17).and_hms(12, 0, 0),
421        };
422        let ie = InformationElement::Header(header.into());
423        let mut cursor = Cursor::new(Vec::new());
424        ie.write_to(&mut cursor).unwrap();
425        cursor.set_position(0);
426        assert_eq!(ie, InformationElement::read_single(&mut cursor).unwrap());
427    }
428
429    #[test]
430    fn header_time_of_session_too_old() {
431        let header = mo::Header {
432            auto_id: 1,
433            imei: [0; 15],
434            session_status: mo::SessionStatus::Ok,
435            momsn: 1,
436            mtmsn: 1,
437            time_of_session: Utc.ymd(1969, 12, 31).and_hms(23, 59, 59),
438        };
439        assert!(InformationElement::Header(header.into())
440            .write_to(&mut Cursor::new(Vec::new()))
441            .is_err());
442    }
443
444    #[test]
445    fn roundtrip_payload() {
446        let payload = vec![1];
447        let ie = InformationElement::MOPayload(payload);
448        let mut cursor = Cursor::new(Vec::new());
449        ie.write_to(&mut cursor).unwrap();
450        cursor.set_position(0);
451        assert_eq!(ie, InformationElement::read_single(cursor).unwrap());
452    }
453
454    #[test]
455    fn payload_too_long() {
456        use std::u16;
457        let payload = vec![0; u16::MAX as usize + 1];
458        assert!(InformationElement::MOPayload(payload)
459            .write_to(&mut Cursor::new(Vec::new()))
460            .is_err());
461    }
462
463    #[test]
464    fn roundtrip_location_information() {
465        let ie = InformationElement::LocationInformation(LocationInformation::new(
466            0,
467            (60, 1111),
468            (60, 1111),
469            None,
470        ));
471        let mut cursor = Cursor::new(Vec::new());
472        ie.write_to(&mut cursor).unwrap();
473        cursor.set_position(0);
474        assert_eq!(ie, ie);
475    }
476}