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 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#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub enum Header {
25 MOHeader(mo::Header),
27 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#[derive(Clone, Debug, PartialEq, Eq)]
108pub enum Status {
109 MOStatus(mo::ConfirmationStatus),
111 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#[derive(Clone, Debug, PartialEq, Eq)]
148pub enum InformationElement {
149 Header(Header),
151 MOPayload(Vec<u8>),
153 MTPayload(Vec<u8>),
155 Status(Status),
157 LocationInformation(LocationInformation),
159}
160
161impl InformationElement {
162 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 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 pub fn is_empty(&self) -> bool {
244 match *self {
245 InformationElement::MOPayload(ref payload) => payload.is_empty(),
246 _ => false,
247 }
248 }
249
250 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}