1use crate::codecs::{GSM, UCS2};
2use crate::elements::{Date, Number, Toa, TypeOfAddress};
3use crate::{PDUError, Result};
4
5use std::io::{Cursor, Read, Seek, SeekFrom};
6
7struct PDUReader {
9 cursor: Cursor<Vec<u8>>,
10}
11
12impl PDUReader {
13 fn new(data: &str) -> Result<Self> {
14 if !data.len().is_multiple_of(2) {
15 return Err(PDUError::OddLength);
16 }
17 let bytes = hex::decode(data)?;
18 Ok(PDUReader {
19 cursor: Cursor::new(bytes),
20 })
21 }
22
23 fn read_hex(&mut self, len: usize) -> Result<String> {
25 let mut buffer = vec![0; len];
26 if self.cursor.read_exact(&mut buffer).is_err() {
27 return Err(PDUError::EndOfPdu);
28 }
29 Ok(hex::encode_upper(buffer))
30 }
31
32 fn read_hex_available(&mut self, len: usize) -> Result<String> {
34 let mut buffer = vec![0; len];
35 let bytes_read = self
36 .cursor
37 .read(&mut buffer)
38 .map_err(|_| PDUError::EndOfPdu)?;
39 buffer.truncate(bytes_read);
40 Ok(hex::encode_upper(buffer))
41 }
42
43 fn read_octet(&mut self) -> Result<u8> {
45 let hex_str = self.read_hex(1)?;
46 Ok(u8::from_str_radix(&hex_str, 16).unwrap_or(0))
47 }
48
49 fn position(&self) -> u64 {
51 self.cursor.position()
52 }
53
54 fn seek(&mut self, pos: u64) -> Result<u64> {
56 self.cursor
57 .seek(SeekFrom::Start(pos))
58 .map_err(|_| PDUError::EndOfPdu)
59 }
60}
61
62#[derive(Debug, Default)]
64pub struct DecodeContext {
65 pub header: Option<PDUHeaderDecoded>,
66 pub dcs: Option<DcsDecoded>,
67}
68
69#[derive(Debug, PartialEq)]
71pub struct AddressDecoded {
72 pub length: u8,
73 pub toa: Toa,
74 pub number: String,
75}
76
77pub struct Address;
78
79impl Address {
80 pub fn decode(reader: &mut PDUReader) -> Result<AddressDecoded> {
82 let length = reader.read_octet()?;
83 let toa = TypeOfAddress::decode(&reader.read_hex(1)?)?;
84
85 let octets_to_read = (length as usize).div_ceil(2);
87 let encoded_number = reader.read_hex(octets_to_read)?;
88
89 let number = if toa.ton == "alphanumeric" {
90 GSM::decode(&encoded_number, false)?
91 } else {
92 Number::decode(&encoded_number)?
93 };
94
95 Ok(AddressDecoded {
96 length,
97 toa,
98 number,
99 })
100 }
101}
102
103#[derive(Debug, PartialEq)]
105pub struct SmscDecoded {
106 pub length: u8,
107 pub toa: Option<Toa>,
108 pub number: Option<String>,
109}
110
111pub struct SMSC;
112
113impl SMSC {
114 pub fn decode(reader: &mut PDUReader) -> Result<SmscDecoded> {
116 let length = reader.read_octet()?;
117 if length == 0 {
118 return Ok(SmscDecoded {
119 length: 0,
120 toa: None,
121 number: None,
122 });
123 }
124
125 let toa = TypeOfAddress::decode(&reader.read_hex(1)?)?;
126
127 let octets_to_read = length as usize - 1;
128 let encoded_number = reader.read_hex(octets_to_read)?;
129
130 let number = if toa.ton == "alphanumeric" {
131 GSM::decode(&encoded_number, false)?
132 } else {
133 Number::decode(&encoded_number)?
134 };
135
136 Ok(SmscDecoded {
137 length,
138 toa: Some(toa),
139 number: Some(number),
140 })
141 }
142}
143
144#[derive(Debug, PartialEq, Default, Clone)]
146pub struct PDUHeaderDecoded {
147 pub rp: bool,
148 pub udhi: bool,
149 pub sri: bool,
150 pub lp: bool,
151 pub mms: bool,
152 pub mti: String,
153}
154
155pub struct PDUHeader;
156
157impl PDUHeader {
158 const MTI: [(u8, &'static str); 3] = [
159 (0b00, "deliver"),
160 (0b01, "submit-report"),
161 (0b10, "status-report"),
162 ];
163
164 pub fn decode(reader: &mut PDUReader) -> Result<PDUHeaderDecoded> {
166 let octet = reader.read_octet()?;
167
168 let mti_bits = octet & 0x03;
169 let mti = Self::MTI
170 .iter()
171 .find(|(k, _)| *k == mti_bits)
172 .map(|(_, v)| v.to_string())
173 .ok_or(PDUError::InvalidMti)?;
174
175 Ok(PDUHeaderDecoded {
176 rp: octet & 0x80 != 0, udhi: octet & 0x40 != 0, sri: octet & 0x20 != 0, lp: octet & 0x08 != 0, mms: octet & 0x04 != 0, mti,
182 })
183 }
184}
185
186#[derive(Debug, PartialEq, Default, Clone)]
188pub struct OutgoingPDUHeaderDecoded {
189 pub rp: bool,
190 pub udhi: bool,
191 pub srr: bool,
192 pub vpf: u8,
193 pub rd: bool,
194 pub mti: String,
195}
196
197pub struct OutgoingPDUHeader;
198
199impl OutgoingPDUHeader {
200 const MTI: [(u8, &'static str); 3] = [(0b00, "deliver"), (0b01, "submit"), (0b10, "status")];
201
202 pub fn decode(reader: &mut PDUReader) -> Result<OutgoingPDUHeaderDecoded> {
204 let octet = reader.read_octet()?;
205
206 let mti_bits = octet & 0x03;
207 let mti = Self::MTI
208 .iter()
209 .find(|(k, _)| *k == mti_bits)
210 .map(|(_, v)| v.to_string())
211 .ok_or(PDUError::InvalidMti)?;
212
213 Ok(OutgoingPDUHeaderDecoded {
214 rp: octet & 0x80 != 0, udhi: octet & 0x40 != 0, srr: octet & 0x20 != 0, vpf: (octet & 0x18) >> 3, rd: octet & 0x04 != 0, mti,
220 })
221 }
222}
223
224#[derive(Debug, PartialEq, Default, Clone)]
226pub struct DcsDecoded {
227 pub encoding: String,
228}
229
230pub struct DCS;
231
232impl DCS {
233 pub fn decode(reader: &mut PDUReader) -> Result<DcsDecoded> {
234 let dcs = reader.read_octet()?;
235 let coding = (dcs & 0b1100) >> 2;
236 let encoding = match coding {
237 1 => "binary".to_string(),
238 2 => "ucs2".to_string(),
239 _ => "gsm".to_string(),
240 };
241 Ok(DcsDecoded { encoding })
242 }
243}
244
245#[derive(Debug, PartialEq)]
247pub struct InformationElementDecoded {
248 pub iei: u8,
249 pub length: u8,
250 pub data: serde_json::Value,
251}
252
253pub struct InformationElement;
254
255impl InformationElement {
256 fn concatenated_sms(data: &str, length_bits: u8) -> serde_json::Value {
257 if data.len() < 6 {
259 return serde_json::Value::String(data.to_string());
260 }
261
262 let bytes = hex::decode(data).unwrap_or_default();
263 if bytes.len() != 3 {
264 return serde_json::Value::String(data.to_string());
265 }
266
267 let reference = if length_bits == 16 {
268 (bytes[0] as u16) << 8 | (bytes[1] as u16)
269 } else {
270 bytes[0] as u16
271 };
272
273 let (parts_count, part_number) = if length_bits == 16 {
274 (bytes[2], bytes[3]) } else {
276 (bytes[1], bytes[2])
277 };
278
279 serde_json::json!({
280 "reference": reference,
281 "parts_count": parts_count,
282 "part_number": part_number,
283 })
284 }
285
286 pub fn decode(reader: &mut PDUReader) -> Result<InformationElementDecoded> {
287 let iei = reader.read_octet()?;
288 let length = reader.read_octet()?;
289 let data_hex = reader.read_hex(length as usize)?;
290
291 let processed_data = match iei {
292 0x00 => Self::concatenated_sms(&data_hex, 8),
293 0x08 => Self::concatenated_sms(&data_hex, 16),
294 _ => serde_json::Value::String(data_hex),
295 };
296
297 Ok(InformationElementDecoded {
298 iei,
299 length,
300 data: processed_data,
301 })
302 }
303}
304
305#[derive(Debug, PartialEq, Default)]
307pub struct UserDataHeaderDecoded {
308 pub length: u8,
309 pub elements: Vec<InformationElementDecoded>,
310}
311
312pub struct UserDataHeader;
313
314impl UserDataHeader {
315 pub fn decode(reader: &mut PDUReader) -> Result<UserDataHeaderDecoded> {
316 let length = reader.read_octet()?;
317 let final_position = reader.position() + length as u64;
318 let mut elements = Vec::new();
319 while reader.position() < final_position {
320 elements.push(InformationElement::decode(reader)?);
321 }
322 Ok(UserDataHeaderDecoded { length, elements })
323 }
324}
325
326#[derive(Debug, PartialEq, Default)]
328pub struct UserDataDecoded {
329 pub header: Option<UserDataHeaderDecoded>,
330 pub data: String,
331 pub warning: Option<String>,
332}
333
334pub struct UserData;
335
336impl UserData {
337 pub fn decode(reader: &mut PDUReader, ctx: &DecodeContext) -> Result<UserDataDecoded> {
338 let length_octets = reader.read_octet()?;
339 let pdu_start = reader.position();
340 let mut header: Option<UserDataHeaderDecoded> = None;
341 let mut header_length_octets: u8 = 0;
342
343 if ctx.header.as_ref().map(|h| h.udhi).unwrap_or(false) {
344 header = Some(UserDataHeader::decode(reader)?);
345 header_length_octets = header.as_ref().unwrap().length + 1;
346 }
347
348 let data_length_octets = length_octets.saturating_sub(header_length_octets);
349 let encoding = ctx
350 .dcs
351 .as_ref()
352 .map(|d| d.encoding.as_str())
353 .unwrap_or("gsm");
354 let mut warning: Option<String> = None;
355
356 let user_data = match encoding {
357 "binary" => {
358 let hex_data = reader.read_hex(data_length_octets as usize)?;
359 hex::decode(hex_data)
360 .map_err(|_| PDUError::InvalidHex(hex::FromHexError::InvalidStringLength))?
361 .iter()
362 .map(|b| format!("{:02X}", b))
363 .collect::<String>()
364 }
365 "gsm" => {
366 reader.seek(pdu_start)?;
367 let header_length_bits = header_length_octets as usize * 8;
368 let data_length_septets = length_octets as usize;
369
370 let data_length_bytes = (data_length_septets * 7).div_ceil(8);
372 let data_hex = reader.read_hex(data_length_bytes)?;
373
374 let decoded_msg = GSM::decode(&data_hex, false)?;
375
376 let header_length_septets = header_length_bits.div_ceil(7);
378
379 if decoded_msg.len() > header_length_septets {
380 decoded_msg[header_length_septets..].to_string()
381 } else {
382 decoded_msg.to_string() }
384 }
385 "ucs2" => {
386 let expected_hex_len = 2 * data_length_octets as usize;
387 let hex_data = reader.read_hex_available(data_length_octets as usize)?;
388
389 let is_truncated = hex_data.len() < expected_hex_len;
390
391 if is_truncated {
392 warning = Some(PDUError::UserDataTruncated.to_string());
393 let trunc_len = hex_data.len() - (hex_data.len() % 4);
394 UCS2::decode(&hex_data[..trunc_len])? + "…"
395 } else {
396 UCS2::decode(&hex_data)?
397 }
398 }
399 _ => return Err(PDUError::NonRecognizedEncoding(encoding.to_string())),
400 };
401
402 Ok(UserDataDecoded {
403 header,
404 data: user_data,
405 warning,
406 })
407 }
408}
409
410#[derive(Debug, PartialEq)]
412pub struct SmsDeliverDecoded {
413 pub smsc: SmscDecoded,
414 pub header: PDUHeaderDecoded,
415 pub sender: AddressDecoded,
416 pub pid: u8,
417 pub dcs: DcsDecoded,
418 pub scts: chrono::DateTime<chrono::Utc>,
419 pub user_data: UserDataDecoded,
420}
421
422pub struct SMSDeliver;
423
424impl SMSDeliver {
425 pub fn decode(data: &str) -> Result<SmsDeliverDecoded> {
427 let mut reader = PDUReader::new(data)?;
428 let smsc = SMSC::decode(&mut reader)?;
429 let header = PDUHeader::decode(&mut reader)?;
430 let sender = Address::decode(&mut reader)?;
431 let pid = reader.read_octet()?;
432 let dcs = DCS::decode(&mut reader)?;
433 let scts_hex = reader.read_hex(7)?;
434 let scts = Date::decode(&scts_hex)?;
435
436 let ctx = DecodeContext {
437 header: Some(header.clone()),
438 dcs: Some(dcs.clone()),
439 };
440 let user_data = UserData::decode(&mut reader, &ctx)?;
441
442 Ok(SmsDeliverDecoded {
443 smsc,
444 header,
445 sender,
446 pid,
447 dcs,
448 scts,
449 user_data,
450 })
451 }
452}
453
454#[derive(Debug, PartialEq)]
456pub struct SmsSubmitDecoded {
457 pub smsc: SmscDecoded,
458 pub header: OutgoingPDUHeaderDecoded,
459 pub message_ref: u8,
460 pub recipient: AddressDecoded,
461 pub pid: u8,
462 pub dcs: DcsDecoded,
463 pub vp: Option<u8>,
464 pub validity_minutes: Option<u16>,
465 pub validity_hours: Option<u16>,
466 pub validity_days: Option<u8>,
467 pub validity_weeks: Option<u8>,
468 pub vp_date: Option<chrono::DateTime<chrono::Utc>>,
469 pub user_data: UserDataDecoded,
470}
471
472pub struct SMSSubmit;
473
474impl SMSSubmit {
475 pub fn decode(data: &str) -> Result<SmsSubmitDecoded> {
477 let mut reader = PDUReader::new(data)?;
478 let smsc = SMSC::decode(&mut reader)?;
479 let header = OutgoingPDUHeader::decode(&mut reader)?;
480 let message_ref = reader.read_octet()?;
481 let recipient = Address::decode(&mut reader)?;
482 let pid = reader.read_octet()?;
483 let dcs = DCS::decode(&mut reader)?;
484
485 let mut decoded = SmsSubmitDecoded {
486 smsc,
487 header: header.clone(),
488 message_ref,
489 recipient,
490 pid,
491 dcs: dcs.clone(),
492 vp: None,
493 validity_minutes: None,
494 validity_hours: None,
495 validity_days: None,
496 validity_weeks: None,
497 vp_date: None,
498 user_data: UserDataDecoded::default(),
499 };
500
501 if header.vpf == 0 {
502 } else if header.vpf == 2 {
504 let vp = reader.read_octet()?;
506 decoded.vp = Some(vp);
507 if vp <= 143 {
508 decoded.validity_minutes = Some(vp as u16 * 5);
509 } else if vp <= 167 {
510 decoded.validity_hours = Some(12 + (vp - 143) as u16 / 2);
511 } else if vp <= 196 {
512 decoded.validity_days = Some(vp - 166);
513 } else {
514 decoded.validity_weeks = Some(vp - 192);
515 }
516 } else if header.vpf == 3 {
517 let vp_hex = reader.read_hex(7)?;
519 decoded.vp_date = Some(Date::decode(&vp_hex)?);
520 } else {
521 reader.read_hex(7)?;
523 }
524
525 let user_data_ctx = DecodeContext {
526 header: Some(PDUHeaderDecoded {
527 udhi: decoded.header.udhi,
528 ..Default::default()
529 }),
530 dcs: Some(decoded.dcs.clone()),
531 };
532 decoded.user_data = UserData::decode(&mut reader, &user_data_ctx)?;
533
534 Ok(decoded)
535 }
536}
537
538#[cfg(test)]
539mod tests {
540 use super::*;
541
542 #[test]
543 fn test_decode_truncated_ucs2() -> Result<()> {
544 let pdu = "0891683110304105F1240D91683167414052F700081270115183942344597D70E6597D70E651CF80A551CF80A55C";
545
546 let decoded_data = SMSDeliver::decode(pdu)?;
547
548 assert_eq!(decoded_data.user_data.data, "好烦好烦减肥减肥…");
552
553 assert!(decoded_data.user_data.warning.is_some());
555
556 Ok(())
557 }
558}