additional fields

This commit is contained in:
eta 2022-12-31 14:35:01 +00:00
parent 11b41c3281
commit 1bb6e5cff8
2 changed files with 51 additions and 9 deletions

View File

@ -91,7 +91,7 @@ fn main() -> anyhow::Result<()> {
} else {
eprintln!("[+] checksum ok");
}
let ticket = Rsp6Ticket::decode(unpadded)?;
let ticket = Rsp6Ticket::decode(unpadded, issuer_id.into())?;
serde_json::to_writer_pretty(std::io::stdout(), &ticket)?;
return Ok(());
} else {

View File

@ -17,6 +17,13 @@ pub enum CouponType {
ReturnInbound = 3,
}
#[derive(Copy, Clone, Debug, Serialize)]
pub enum DepartTimeFlag {
Mystery = 1,
Specific = 2,
Suggested = 3,
}
#[derive(Clone, Debug, Serialize)]
pub struct TicketPurchaseDetails {
pub purchase_time: PrimitiveDateTime,
@ -32,6 +39,7 @@ pub struct Reservation {
pub seat_number: u8,
pub seat_letter: Option<char>,
}
impl Reservation {
pub fn decode(resv: &BitSlice<u8, Msb0>) -> anyhow::Result<Self> {
if resv.len() != 45 {
@ -57,6 +65,7 @@ impl Reservation {
#[derive(Clone, Debug, Serialize)]
pub struct Rsp6Ticket {
pub manually_inspect: bool,
pub issuer_id: String,
pub ticket_reference: String,
pub checksum: char,
pub version: u8,
@ -71,7 +80,7 @@ pub struct Rsp6Ticket {
pub discount_code: u16,
pub route_code: u32,
pub start_date: Date,
pub depart_time_flag: u8,
pub depart_time_flag: Option<DepartTimeFlag>,
pub depart_time: Time,
pub passenger_id: Option<String>,
pub passenger_name: Option<String>,
@ -82,6 +91,9 @@ pub struct Rsp6Ticket {
pub purchase_details: Option<TicketPurchaseDetails>,
pub reservations: Vec<Reservation>,
pub free_text: Option<String>,
pub osi_nlc: Option<String>,
pub mystery_flag: bool,
pub mystery_header: String,
}
impl Rsp6Ticket {
@ -132,10 +144,12 @@ impl Rsp6Ticket {
Some(format!("{}{:04}", prefix, (id % 10000)))
}
pub fn decode(tkt: &[u8]) -> anyhow::Result<Self> {
pub fn decode(tkt: &[u8], issuer_id: String) -> anyhow::Result<Self> {
let bit_tkt = tkt.view_bits::<Msb0>();
let manually_inspect = bit_tkt[0];
let mystery_header: u8 = bit_tkt[1..8].load_be();
let mystery_header = format!("{:07b}", mystery_header);
let ticket_reference = Self::base64(tkt, 8, 62);
let checksum = Self::base64(tkt, 62, 68).chars().next().unwrap();
let version: u8 = bit_tkt[68..72].load_be();
@ -162,18 +176,29 @@ impl Rsp6Ticket {
let start_time_secs: u32 = bit_tkt[225..236].load_be();
let start_time: PrimitiveDateTime =
CapitalismDateTime::new(start_time_days, start_time_secs).into();
let depart_time_flag: u8 = bit_tkt[236..238].load_be();
let depart_time_flag = match bit_tkt[236..238].load_be::<u8>() {
0 => None,
1 => Some(DepartTimeFlag::Mystery),
2 => Some(DepartTimeFlag::Specific),
3 => Some(DepartTimeFlag::Suggested),
_ => unreachable!(), // only 2-bit int
};
let depart_time = start_time.time();
let start_date = start_time.date();
let passenger_id = Self::decode_passenger_id(bit_tkt[238..255].load_be());
let passenger_name = Self::base64(tkt, 255, 327);
let passenger_name = (!passenger_name.trim().is_empty()).then_some(passenger_name);
let passenger_name =
(!passenger_name.trim().is_empty()).then(|| passenger_name.trim().to_owned());
let passenger_gender: u8 = bit_tkt[327..329].load_be();
let passenger_gender = (passenger_gender != 0).then_some(passenger_gender);
let restriction_code = Self::base64(tkt, 329, 347);
let restriction_code = (!restriction_code.trim().is_empty()).then_some(restriction_code);
let osi_nlc = Self::base64(tkt, 347, 371);
let osi_nlc = (!osi_nlc.trim().is_empty()).then(|| osi_nlc.trim().to_owned());
let mystery_flag = bit_tkt[371];
let restriction_code =
(!restriction_code.trim().is_empty()).then(|| restriction_code.trim().to_owned());
let bidirectional = bit_tkt[372];
let limited_duration = Self::decode_limited_duration(bit_tkt[379..383].load_be());
@ -186,8 +211,8 @@ impl Rsp6Ticket {
CapitalismDateTime::new(purchase_time_days, purchase_time_secs).into();
let price_pence: u32 = bit_tkt[415..436].load_be();
let purchase_reference = Self::base64(tkt, 449, 497);
let purchase_reference =
(!purchase_reference.trim().is_empty()).then_some(purchase_reference);
let purchase_reference = (!purchase_reference.trim().is_empty())
.then(|| purchase_reference.trim().to_owned());
let mut days_of_validity = bit_tkt[497..506].load_be();
if days_of_validity == 0 {
days_of_validity = 1;
@ -212,8 +237,22 @@ impl Rsp6Ticket {
})
.collect::<Result<Vec<_>, _>>()?;
let has_free_text = bit_tkt[385];
let free_text_is_extended = bit_tkt[383];
let mut free_text = None;
if has_free_text {
let reservations_end = reservations_start + (45 * reservations_count as usize);
let end = if free_text_is_extended { 863 } else { 783 };
let length = 6 * ((end - reservations_end) / 6);
let text = Self::base64(tkt, reservations_end, reservations_end + length);
if !text.trim().is_empty() {
free_text = Some(text.trim().to_owned());
}
}
Ok(Self {
manually_inspect,
issuer_id,
ticket_reference,
checksum,
version,
@ -238,7 +277,10 @@ impl Rsp6Ticket {
limited_duration,
purchase_details,
reservations,
free_text: None,
free_text,
osi_nlc,
mystery_flag,
mystery_header,
})
}
}