mirror of
https://gitea.tendokyu.moe/ppc/amnet.git
synced 2026-09-29 02:08:03 +03:00
95 lines
2.6 KiB
Rust
95 lines
2.6 KiB
Rust
use std::{sync::{Mutex, OnceLock}, time::{Duration, Instant}};
|
|
|
|
use tracing::debug;
|
|
|
|
use crate::utils::{parse_access_code, parse_idm_hex};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub (crate) struct StoredCard {
|
|
pub access_code: [u8; 10],
|
|
pub card_idm: Option<[u8; 8]>,
|
|
|
|
pub expiry_time: Instant
|
|
}
|
|
|
|
impl StoredCard {
|
|
pub fn is_expired(&self) -> bool {
|
|
Instant::now() > self.expiry_time
|
|
}
|
|
}
|
|
|
|
pub (crate) enum CardPresentResult {
|
|
Accepted,
|
|
Rejected(String /* reason */),
|
|
SlotUnavailable(Instant /* release time */),
|
|
}
|
|
|
|
static PRESENTER: OnceLock<CardPresenter> = OnceLock::new();
|
|
|
|
#[derive(Debug)]
|
|
pub (crate) struct CardPresenter {
|
|
current_card: Mutex<Option<StoredCard>>,
|
|
}
|
|
|
|
impl Default for CardPresenter {
|
|
fn default() -> Self {
|
|
Self {
|
|
current_card: Mutex::new(None),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CardPresenter {
|
|
pub fn instance() -> &'static CardPresenter {
|
|
PRESENTER.get_or_init(|| CardPresenter::default())
|
|
}
|
|
|
|
pub fn request_card(&self, require_idm: Option<bool>) -> Option<StoredCard> {
|
|
let mut guard = self.current_card.lock().unwrap();
|
|
let card = guard.as_ref()?;
|
|
|
|
if let Some(require) = require_idm && card.card_idm.is_some() != require {
|
|
return None;
|
|
}
|
|
|
|
let card = guard.take();
|
|
|
|
if let Some(ref stored_card) = card && stored_card.is_expired() {
|
|
debug!("Stored card has expired and will not be provided");
|
|
return None;
|
|
};
|
|
|
|
card
|
|
}
|
|
|
|
pub fn present_card(&self, access_code: &String, idm_hex: Option<&String>, validity: Duration) -> CardPresentResult {
|
|
let mut guard = self.current_card.lock().unwrap();
|
|
|
|
if let Some(card) = guard.as_ref() && !card.is_expired() {
|
|
return CardPresentResult::SlotUnavailable(card.expiry_time);
|
|
}
|
|
|
|
let access_code_bytes = match parse_access_code(access_code) {
|
|
Some(bytes) => bytes,
|
|
None => return CardPresentResult::Rejected("Invalid access code format".to_string()),
|
|
};
|
|
|
|
let mut card_idm_bytes = None;
|
|
if let Some(idm_hex) = idm_hex {
|
|
match parse_idm_hex(idm_hex) {
|
|
Some(bytes) => card_idm_bytes = Some(bytes),
|
|
None => return CardPresentResult::Rejected("Invalid IDm format".to_string()),
|
|
}
|
|
}
|
|
|
|
let stored_card = StoredCard {
|
|
access_code: access_code_bytes,
|
|
card_idm: card_idm_bytes,
|
|
expiry_time: Instant::now() + validity,
|
|
};
|
|
|
|
*guard = Some(stored_card);
|
|
|
|
CardPresentResult::Accepted
|
|
}
|
|
} |