1use super::gsm7::{self, Language, Tables};
10use super::{hex_decode, ucs2};
11use serde::{Deserialize, Serialize};
12use std::fmt;
13
14mod encode;
15pub use encode::*;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[non_exhaustive]
19pub enum PduError {
20 InvalidHex,
22 Truncated(&'static str),
24 Invalid(&'static str),
26 ReservedMessageType,
28 Unencodable(char),
30 TooManyParts,
32 MissingConcatRef,
35}
36
37impl fmt::Display for PduError {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 match self {
40 Self::InvalidHex => f.write_str("PDU is not a valid hex string"),
41 Self::Truncated(field) => write!(f, "PDU truncated in {field}"),
42 Self::Invalid(field) => write!(f, "invalid {field}"),
43 Self::ReservedMessageType => f.write_str("reserved TP-MTI"),
44 Self::Unencodable(c) => write!(f, "character {c:?} is not encodable"),
45 Self::TooManyParts => f.write_str("message needs more than 255 parts"),
46 Self::MissingConcatRef => {
47 f.write_str("message needs concatenation: set a concatenation reference")
48 }
49 }
50 }
51}
52
53impl std::error::Error for PduError {}
54
55type Result<T> = std::result::Result<T, PduError>;
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
61#[serde(rename_all = "snake_case")]
62pub enum Ton {
63 Unknown,
64 International,
65 National,
66 NetworkSpecific,
67 Subscriber,
68 Alphanumeric,
69 Abbreviated,
70 Reserved,
71}
72
73impl Ton {
74 pub fn from_toa(toa: u8) -> Self {
75 match (toa >> 4) & 7 {
76 0 => Self::Unknown,
77 1 => Self::International,
78 2 => Self::National,
79 3 => Self::NetworkSpecific,
80 4 => Self::Subscriber,
81 5 => Self::Alphanumeric,
82 6 => Self::Abbreviated,
83 _ => Self::Reserved,
84 }
85 }
86}
87
88pub const TOA_INTERNATIONAL: u8 = 0x91;
90pub const TOA_UNKNOWN: u8 = 0x81;
92pub const TOA_NATIONAL: u8 = 0xA1;
94pub const TOA_ALPHANUMERIC: u8 = 0xD0;
96
97#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
103pub struct Address {
104 pub toa: u8,
105 pub value: String,
106}
107
108impl Address {
109 pub fn ton(&self) -> Ton {
110 Ton::from_toa(self.toa)
111 }
112
113 pub fn npi(&self) -> u8 {
115 self.toa & 0x0F
116 }
117}
118
119impl fmt::Display for Address {
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 if self.ton() == Ton::International && !self.value.is_empty() {
122 f.write_str("+")?;
123 }
124 f.write_str(&self.value)
125 }
126}
127
128const SEMI_OCTET: &[u8; 15] = b"0123456789*#abc";
129
130pub fn decode_semi_octets(bytes: &[u8], digits: usize) -> String {
132 bytes
133 .iter()
134 .flat_map(|b| [b & 0x0F, b >> 4])
135 .take(digits)
136 .take_while(|&n| n != 0x0F)
137 .map(|n| char::from(SEMI_OCTET[usize::from(n)]))
138 .collect()
139}
140
141pub fn encode_semi_octets(digits: &str) -> std::result::Result<Vec<u8>, char> {
144 let nibbles = digits
145 .chars()
146 .map(|c| {
147 SEMI_OCTET
148 .iter()
149 .position(|&d| char::from(d) == c.to_ascii_lowercase())
150 .map(|n| n as u8)
151 .ok_or(c)
152 })
153 .collect::<std::result::Result<Vec<u8>, char>>()?;
154 Ok(nibbles
155 .chunks(2)
156 .map(|p| p[0] | (p.get(1).copied().unwrap_or(0x0F) << 4))
157 .collect())
158}
159
160fn decode_address_value(toa: u8, body: &[u8], semi_octets: usize) -> String {
161 if Ton::from_toa(toa) == Ton::Alphanumeric {
162 let septets = gsm7::unpack(body, 0, semi_octets * 4 / 7);
163 gsm7::decode(&septets, Tables::DEFAULT)
164 } else {
165 decode_semi_octets(body, semi_octets)
166 }
167}
168
169pub fn decode_address(bytes: &[u8]) -> Result<(Address, usize)> {
172 let mut r = Reader::new(bytes);
173 let digits = usize::from(r.u8("address length")?);
174 if digits > 20 {
175 return Err(PduError::Invalid("address length"));
176 }
177 let toa = r.u8("address type")?;
178 let body = r.take(digits.div_ceil(2), "address")?;
179 let value = decode_address_value(toa, body, digits);
180 Ok((Address { toa, value }, r.pos))
181}
182
183pub fn decode_smsc(bytes: &[u8]) -> Result<(Option<Address>, usize)> {
185 let mut r = Reader::new(bytes);
186 let len = usize::from(r.u8("SMSC length")?);
187 if len == 0 {
188 return Ok((None, 1));
189 }
190 if len > 11 {
191 return Err(PduError::Invalid("SMSC length"));
192 }
193 let toa = r.u8("SMSC type")?;
194 let body = r.take(len - 1, "SMSC address")?;
195 let value = decode_address_value(toa, body, body.len() * 2);
196 Ok((Some(Address { toa, value }), r.pos))
197}
198
199#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
204pub struct Timestamp {
205 pub year: u16,
206 pub month: u8,
207 pub day: u8,
208 pub hour: u8,
209 pub minute: u8,
210 pub second: u8,
211 pub tz_quarters: i8,
212}
213
214fn bcd(b: u8) -> Option<u8> {
215 let (tens, units) = (b & 0x0F, b >> 4);
216 (tens <= 9 && units <= 9).then_some(tens * 10 + units)
217}
218
219fn to_bcd(v: u8) -> u8 {
220 (v % 10) << 4 | (v / 10)
221}
222
223fn days_in_month(year: u16, month: u8) -> u8 {
224 match month {
225 2 if (year % 4 == 0 && year % 100 != 0) || year % 400 == 0 => 29,
226 2 => 28,
227 4 | 6 | 9 | 11 => 30,
228 _ => 31,
229 }
230}
231
232impl Timestamp {
233 pub fn decode(b: &[u8; 7]) -> Option<Self> {
235 let yy = bcd(b[0])?;
236 let year = if yy >= 70 { 1900 } else { 2000 } + u16::from(yy);
237 let (month, day, hour, minute, second) =
238 (bcd(b[1])?, bcd(b[2])?, bcd(b[3])?, bcd(b[4])?, bcd(b[5])?);
239 let tz_quarters = decode_tz(b[6])?;
240 let ok = (1..=12).contains(&month)
241 && day >= 1
242 && day <= days_in_month(year, month)
243 && hour < 24
244 && minute < 60
245 && second < 60;
246 ok.then_some(Timestamp {
247 year,
248 month,
249 day,
250 hour,
251 minute,
252 second,
253 tz_quarters,
254 })
255 }
256
257 pub fn encode(&self) -> Option<[u8; 7]> {
260 if !(1970..=2069).contains(&self.year) || self.tz_quarters.unsigned_abs() > 79 {
261 return None;
262 }
263 let out = [
264 to_bcd((self.year % 100) as u8),
265 to_bcd(self.month),
266 to_bcd(self.day),
267 to_bcd(self.hour),
268 to_bcd(self.minute),
269 to_bcd(self.second),
270 to_bcd(self.tz_quarters.unsigned_abs()) | if self.tz_quarters < 0 { 0x08 } else { 0 },
271 ];
272 (Self::decode(&out) == Some(*self)).then_some(out)
274 }
275
276 pub fn offset_minutes(&self) -> i32 {
277 i32::from(self.tz_quarters) * 15
278 }
279
280 pub fn unix_utc(&self) -> i64 {
282 let (m, d) = (i64::from(self.month), i64::from(self.day));
284 let y = i64::from(self.year) - i64::from(m <= 2);
285 let era = y.div_euclid(400);
286 let yoe = y - era * 400;
287 let doy = (153 * (m + if m > 2 { -3 } else { 9 }) + 2) / 5 + d - 1;
288 let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
289 let days = era * 146_097 + doe - 719_468;
290 days * 86_400
291 + i64::from(self.hour) * 3600
292 + i64::from(self.minute) * 60
293 + i64::from(self.second)
294 - i64::from(self.offset_minutes()) * 60
295 }
296}
297
298pub fn decode_tz(b: u8) -> Option<i8> {
300 let q = bcd(b & 0xF7)? as i8;
301 Some(if b & 0x08 != 0 { -q } else { q })
302}
303
304impl fmt::Display for Timestamp {
305 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
307 let off = self.offset_minutes();
308 write!(
309 f,
310 "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}{}{:02}:{:02}",
311 self.year,
312 self.month,
313 self.day,
314 self.hour,
315 self.minute,
316 self.second,
317 if off < 0 { '-' } else { '+' },
318 off.unsigned_abs() / 60,
319 off.unsigned_abs() % 60
320 )
321 }
322}
323
324#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
328pub enum Validity {
329 Relative(u8),
331 Absolute(Timestamp),
332 Enhanced([u8; 7]),
334}
335
336impl Validity {
337 pub fn vpf(&self) -> u8 {
339 match self {
340 Self::Relative(_) => 2,
341 Self::Absolute(_) => 3,
342 Self::Enhanced(_) => 1,
343 }
344 }
345}
346
347pub fn relative_minutes(vp: u8) -> u32 {
349 let v = u32::from(vp);
350 match vp {
351 0..=143 => (v + 1) * 5,
352 144..=167 => 720 + (v - 143) * 30,
353 168..=196 => (v - 166) * 1440,
354 _ => (v - 192) * 10_080,
355 }
356}
357
358pub fn relative_from_minutes(minutes: u32) -> Option<u8> {
361 let v = match minutes {
362 0..=720 => minutes.div_ceil(5).max(1) - 1,
363 721..=1440 => 143 + (minutes - 720).div_ceil(30),
364 1441..=43_200 => 166 + minutes.div_ceil(1440),
365 _ => 192 + minutes.div_ceil(10_080),
366 };
367 u8::try_from(v).ok()
368}
369
370#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
373#[serde(rename_all = "lowercase")]
374pub enum Alphabet {
375 Gsm7,
376 Bit8,
377 Ucs2,
378}
379
380#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
381#[serde(rename_all = "lowercase")]
382pub enum MwiKind {
383 Voicemail,
384 Fax,
385 Email,
386 Other,
387}
388
389#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
391pub struct Mwi {
392 pub active: bool,
393 pub kind: MwiKind,
394 pub store: bool,
396}
397
398#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
400pub struct Dcs {
401 pub raw: u8,
402 pub alphabet: Alphabet,
403 pub class: Option<u8>,
405 pub compressed: bool,
406 pub auto_delete: bool,
408 pub mwi: Option<Mwi>,
409 pub reserved: bool,
411}
412
413impl Dcs {
414 pub fn decode(raw: u8) -> Self {
415 let mut d = Dcs {
416 raw,
417 alphabet: Alphabet::Gsm7,
418 class: None,
419 compressed: false,
420 auto_delete: false,
421 mwi: None,
422 reserved: false,
423 };
424 match raw >> 4 {
425 0x0..=0x7 => {
426 d.auto_delete = raw & 0x40 != 0;
427 d.compressed = raw & 0x20 != 0;
428 d.class = (raw & 0x10 != 0).then_some(raw & 3);
429 match (raw >> 2) & 3 {
430 0 => {}
431 1 => d.alphabet = Alphabet::Bit8,
432 2 => d.alphabet = Alphabet::Ucs2,
433 _ => d.reserved = true,
434 }
435 }
436 0xC..=0xE => {
437 d.alphabet = if raw >> 4 == 0xE {
438 Alphabet::Ucs2
439 } else {
440 Alphabet::Gsm7
441 };
442 d.mwi = Some(Mwi {
443 active: raw & 0x08 != 0,
444 kind: [
445 MwiKind::Voicemail,
446 MwiKind::Fax,
447 MwiKind::Email,
448 MwiKind::Other,
449 ][usize::from(raw & 3)],
450 store: raw >> 4 != 0xC,
451 });
452 }
453 0xF => {
454 if raw & 0x04 != 0 {
455 d.alphabet = Alphabet::Bit8;
456 }
457 d.class = Some(raw & 3);
458 }
459 _ => d.reserved = true,
460 }
461 d
462 }
463
464 pub fn encode(alphabet: Alphabet, class: Option<u8>) -> u8 {
466 let a = match alphabet {
467 Alphabet::Gsm7 => 0x00,
468 Alphabet::Bit8 => 0x04,
469 Alphabet::Ucs2 => 0x08,
470 };
471 a | class.map_or(0, |c| 0x10 | (c & 3))
472 }
473}
474
475#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
479pub struct Ie {
480 pub id: u8,
481 pub data: Vec<u8>,
482}
483
484#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
486pub struct Udh {
487 pub ies: Vec<Ie>,
488}
489
490#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
491pub struct Concat {
492 pub reference: u16,
493 pub total: u8,
494 pub seq: u8,
495 pub ref_16bit: bool,
497}
498
499#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
500pub struct Ports {
501 pub dest: u16,
502 pub orig: u16,
503 pub wide: bool,
505}
506
507pub const IEI_CONCAT_8: u8 = 0x00;
508pub const IEI_PORTS_8: u8 = 0x04;
509pub const IEI_PORTS_16: u8 = 0x05;
510pub const IEI_CONCAT_16: u8 = 0x08;
511pub const IEI_SINGLE_SHIFT: u8 = 0x24;
512pub const IEI_LOCKING_SHIFT: u8 = 0x25;
513
514impl Udh {
515 pub fn parse(mut bytes: &[u8]) -> Result<Self> {
517 let mut ies = Vec::new();
518 while let [id, len, rest @ ..] = bytes {
519 let len = usize::from(*len);
520 let data = rest.get(..len).ok_or(PduError::Truncated("UDH element"))?;
521 ies.push(Ie {
522 id: *id,
523 data: data.to_vec(),
524 });
525 bytes = &rest[len..];
526 }
527 if bytes.is_empty() {
528 Ok(Udh { ies })
529 } else {
530 Err(PduError::Truncated("UDH element"))
531 }
532 }
533
534 pub fn encode(&self) -> Result<Vec<u8>> {
538 let mut out = vec![0];
539 for ie in &self.ies {
540 let len = u8::try_from(ie.data.len()).map_err(|_| PduError::Invalid("UDH length"))?;
541 out.push(ie.id);
542 out.push(len);
543 out.extend_from_slice(&ie.data);
544 }
545 out[0] = u8::try_from(out.len() - 1).map_err(|_| PduError::Invalid("UDH length"))?;
546 Ok(out)
547 }
548
549 fn ie(&self, id: u8) -> Option<&[u8]> {
550 self.ies
551 .iter()
552 .rev()
553 .find(|ie| ie.id == id)
554 .map(|ie| ie.data.as_slice())
555 }
556
557 pub fn concat(&self) -> Option<Concat> {
559 let c = match (self.ie(IEI_CONCAT_8), self.ie(IEI_CONCAT_16)) {
560 (_, Some(&[hi, lo, total, seq])) => Concat {
561 reference: u16::from_be_bytes([hi, lo]),
562 total,
563 seq,
564 ref_16bit: true,
565 },
566 (Some(&[r, total, seq]), _) => Concat {
567 reference: u16::from(r),
568 total,
569 seq,
570 ref_16bit: false,
571 },
572 _ => return None,
573 };
574 (c.total > 0 && c.seq > 0 && c.seq <= c.total).then_some(c)
575 }
576
577 pub fn ports(&self) -> Option<Ports> {
578 match (self.ie(IEI_PORTS_8), self.ie(IEI_PORTS_16)) {
579 (_, Some(&[a, b, c, d])) => Some(Ports {
580 dest: u16::from_be_bytes([a, b]),
581 orig: u16::from_be_bytes([c, d]),
582 wide: true,
583 }),
584 (Some(&[dest, orig]), _) => Some(Ports {
585 dest: u16::from(dest),
586 orig: u16::from(orig),
587 wide: false,
588 }),
589 _ => None,
590 }
591 }
592
593 pub fn national_languages(&self) -> (Option<u8>, Option<u8>) {
595 let one = |id| self.ie(id).and_then(|d| d.first().copied());
596 (one(IEI_SINGLE_SHIFT), one(IEI_LOCKING_SHIFT))
597 }
598
599 pub fn tables(&self) -> Tables {
602 let (single, locking) = self.national_languages();
603 let lang = |n: Option<u8>| n.and_then(Language::from_nli).unwrap_or_default();
604 let locking = lang(locking);
605 Tables::new(
606 if locking.has_locking_table() {
607 locking
608 } else {
609 Language::Default
610 },
611 lang(single),
612 )
613 }
614}
615
616#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
620pub struct UserData {
621 pub udh: Option<Udh>,
622 pub payload: Vec<u8>,
624 pub text: Option<String>,
626}
627
628pub fn decode_user_data(udl: u8, ud: &[u8], udhi: bool, dcs: &Dcs) -> Result<UserData> {
630 let udl = usize::from(udl);
631 let septets = dcs.alphabet == Alphabet::Gsm7 && !dcs.compressed;
632 let (need, max) = if septets {
633 ((udl * 7).div_ceil(8), 160)
634 } else {
635 (udl, 140)
636 };
637 if udl > max {
638 return Err(PduError::Invalid("TP-UDL"));
639 }
640 let ud = ud.get(..need).ok_or(PduError::Truncated("user data"))?;
641 let (udh, hdr) = if udhi {
642 let udhl = usize::from(*ud.first().ok_or(PduError::Invalid("TP-UDHL"))?);
643 let ies = ud.get(1..=udhl).ok_or(PduError::Invalid("TP-UDHL"))?;
644 (Some(Udh::parse(ies)?), udhl + 1)
645 } else {
646 (None, 0)
647 };
648 let tables = udh.as_ref().map_or(Tables::DEFAULT, Udh::tables);
649 let (payload, text) = if septets {
650 let hdr_septets = (hdr * 8).div_ceil(7);
651 let count = udl
652 .checked_sub(hdr_septets)
653 .ok_or(PduError::Invalid("TP-UDHL"))?;
654 let fill = (hdr_septets * 7 - hdr * 8) as u8;
655 let s = gsm7::unpack(&ud[hdr..], fill, count);
656 let text = gsm7::decode(&s, tables);
657 (s, Some(text))
658 } else {
659 let p = ud[hdr..].to_vec();
660 let text = match (dcs.alphabet, dcs.compressed) {
661 (Alphabet::Ucs2, false) if p.len() % 2 == 1 => {
662 return Err(PduError::Invalid("UCS2 length"))
663 }
664 (Alphabet::Ucs2, false) => Some(ucs2::decode(&p)),
665 _ => None,
666 };
667 (p, text)
668 };
669 Ok(UserData { udh, payload, text })
670}
671
672#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
675pub struct Deliver {
676 pub mms: bool,
678 pub lp: bool,
680 pub sri: bool,
682 pub udhi: bool,
683 pub rp: bool,
684 pub originator: Address,
685 pub pid: u8,
686 pub dcs: Dcs,
687 pub scts: Option<Timestamp>,
689 pub scts_raw: [u8; 7],
690 pub user_data: UserData,
691}
692
693#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
694pub struct Submit {
695 pub rd: bool,
697 pub srr: bool,
699 pub udhi: bool,
700 pub rp: bool,
701 pub mr: u8,
702 pub destination: Address,
703 pub pid: u8,
704 pub dcs: Dcs,
705 pub validity: Option<Validity>,
706 pub user_data: UserData,
707}
708
709#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
710pub struct StatusReport {
711 pub mms: bool,
712 pub lp: bool,
713 pub srq: bool,
715 pub udhi: bool,
716 pub mr: u8,
717 pub recipient: Address,
718 pub scts: Option<Timestamp>,
719 pub scts_raw: [u8; 7],
720 pub discharge: Option<Timestamp>,
721 pub discharge_raw: [u8; 7],
722 pub status: u8,
725 pub pi: Option<u8>,
727 pub pid: Option<u8>,
728 pub dcs: Option<Dcs>,
729 pub user_data: Option<UserData>,
730}
731
732#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
733pub enum Tpdu {
734 Deliver(Deliver),
735 Submit(Submit),
736 StatusReport(StatusReport),
737}
738
739impl Tpdu {
740 pub fn user_data(&self) -> Option<&UserData> {
741 match self {
742 Self::Deliver(d) => Some(&d.user_data),
743 Self::Submit(s) => Some(&s.user_data),
744 Self::StatusReport(r) => r.user_data.as_ref(),
745 }
746 }
747
748 pub fn text(&self) -> Option<&str> {
749 self.user_data()?.text.as_deref()
750 }
751
752 pub fn address(&self) -> &Address {
754 match self {
755 Self::Deliver(d) => &d.originator,
756 Self::Submit(s) => &s.destination,
757 Self::StatusReport(r) => &r.recipient,
758 }
759 }
760
761 pub fn dcs(&self) -> Option<&Dcs> {
762 match self {
763 Self::Deliver(d) => Some(&d.dcs),
764 Self::Submit(s) => Some(&s.dcs),
765 Self::StatusReport(r) => r.dcs.as_ref(),
766 }
767 }
768}
769
770#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
772pub struct Pdu {
773 pub smsc: Option<Address>,
774 pub tpdu: Tpdu,
775}
776
777pub fn decode_hex(hex: &str) -> Result<Pdu> {
779 decode(&hex_decode(hex).ok_or(PduError::InvalidHex)?)
780}
781
782pub fn decode(bytes: &[u8]) -> Result<Pdu> {
785 let (smsc, n) = decode_smsc(bytes)?;
786 Ok(Pdu {
787 smsc,
788 tpdu: decode_tpdu(&bytes[n..])?,
789 })
790}
791
792pub fn decode_tpdu(bytes: &[u8]) -> Result<Tpdu> {
794 let mut r = Reader::new(bytes);
795 let fo = r.u8("first octet")?;
796 let bit = |n: u8| fo & (1 << n) != 0;
797 match fo & 3 {
798 0 => {
799 let originator = r.address()?;
800 let pid = r.u8("TP-PID")?;
801 let dcs = Dcs::decode(r.u8("TP-DCS")?);
802 let scts_raw = r.array("TP-SCTS")?;
803 let user_data = r.user_data(bit(6), &dcs)?;
804 Ok(Tpdu::Deliver(Deliver {
805 mms: bit(2),
806 lp: bit(3),
807 sri: bit(5),
808 udhi: bit(6),
809 rp: bit(7),
810 originator,
811 pid,
812 dcs,
813 scts: Timestamp::decode(&scts_raw),
814 scts_raw,
815 user_data,
816 }))
817 }
818 1 => {
819 let mr = r.u8("TP-MR")?;
820 let destination = r.address()?;
821 let pid = r.u8("TP-PID")?;
822 let dcs = Dcs::decode(r.u8("TP-DCS")?);
823 let validity = match (fo >> 3) & 3 {
824 0 => None,
825 2 => Some(Validity::Relative(r.u8("TP-VP")?)),
826 3 => Some(Validity::Absolute(
827 Timestamp::decode(&r.array("TP-VP")?).ok_or(PduError::Invalid("TP-VP"))?,
828 )),
829 _ => Some(Validity::Enhanced(r.array("TP-VP")?)),
830 };
831 let user_data = r.user_data(bit(6), &dcs)?;
832 Ok(Tpdu::Submit(Submit {
833 rd: bit(2),
834 srr: bit(5),
835 udhi: bit(6),
836 rp: bit(7),
837 mr,
838 destination,
839 pid,
840 dcs,
841 validity,
842 user_data,
843 }))
844 }
845 2 => {
846 let mr = r.u8("TP-MR")?;
847 let recipient = r.address()?;
848 let scts_raw = r.array("TP-SCTS")?;
849 let discharge_raw = r.array("TP-DT")?;
850 let status = r.u8("TP-ST")?;
851 let mut rep = StatusReport {
852 mms: bit(2),
853 lp: bit(3),
854 srq: bit(5),
855 udhi: bit(6),
856 mr,
857 recipient,
858 scts: Timestamp::decode(&scts_raw),
859 scts_raw,
860 discharge: Timestamp::decode(&discharge_raw),
861 discharge_raw,
862 status,
863 pi: None,
864 pid: None,
865 dcs: None,
866 user_data: None,
867 };
868 let mut tail = || -> Result<()> {
872 let pi = r.u8("TP-PI")?;
873 rep.pi = Some(pi);
874 let mut ext = pi;
875 while ext & 0x80 != 0 {
876 ext = r.u8("TP-PI")?;
877 }
878 if pi & 1 != 0 {
879 rep.pid = Some(r.u8("TP-PID")?);
880 }
881 if pi & 2 != 0 {
882 rep.dcs = Some(Dcs::decode(r.u8("TP-DCS")?));
883 }
884 if pi & 4 != 0 {
885 let dcs = rep.dcs.unwrap_or(Dcs::decode(0));
886 rep.user_data = Some(r.user_data(bit(6), &dcs)?);
887 }
888 Ok(())
889 };
890 let _ = tail();
891 Ok(Tpdu::StatusReport(rep))
892 }
893 _ => Err(PduError::ReservedMessageType),
894 }
895}
896
897struct Reader<'a> {
898 b: &'a [u8],
899 pos: usize,
900}
901
902impl<'a> Reader<'a> {
903 fn new(b: &'a [u8]) -> Self {
904 Reader { b, pos: 0 }
905 }
906
907 fn u8(&mut self, field: &'static str) -> Result<u8> {
908 Ok(self.take(1, field)?[0])
909 }
910
911 fn take(&mut self, n: usize, field: &'static str) -> Result<&'a [u8]> {
912 let s = self
913 .b
914 .get(self.pos..self.pos + n)
915 .ok_or(PduError::Truncated(field))?;
916 self.pos += n;
917 Ok(s)
918 }
919
920 fn array(&mut self, field: &'static str) -> Result<[u8; 7]> {
921 let mut a = [0; 7];
922 a.copy_from_slice(self.take(7, field)?);
923 Ok(a)
924 }
925
926 fn address(&mut self) -> Result<Address> {
927 let (a, n) = decode_address(&self.b[self.pos..])?;
928 self.pos += n;
929 Ok(a)
930 }
931
932 fn user_data(&mut self, udhi: bool, dcs: &Dcs) -> Result<UserData> {
933 let udl = self.u8("TP-UDL")?;
934 decode_user_data(udl, &self.b[self.pos..], udhi, dcs)
935 }
936}