#![allow(clippy::large_enum_variant)]
pub mod apdu;
pub mod ffi;
pub mod model;
pub mod options;
pub mod reader;
mod laser;
mod nhso;
mod personal;
pub use options::Options;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
use pcsc::*;
use crate::reader::CardError;
#[derive(Debug)]
pub enum Event {
CardInserted { reader: String },
CardData(model::CardData),
CardRemoved { reader: String },
Error(String),
}
pub struct SmartCard;
impl SmartCard {
pub fn new() -> Self {
Self
}
pub fn list_readers() -> Result<Vec<String>, CardError> {
let ctx = reader::establish_context()?;
reader::list_readers(&ctx)
}
pub fn read(
&self,
reader_name: Option<&str>,
opts: &Options,
) -> Result<model::CardData, CardError> {
let ctx = reader::establish_context()?;
let readers = match reader_name {
Some(name) => vec![name.to_string()],
None => reader::list_readers(&ctx)?,
};
let idx = reader::wait_for_card(&ctx, &readers)?;
let reader = &readers[idx];
self.read_card(&ctx, reader, opts)
}
pub fn start_daemon(opts: Options) -> mpsc::Receiver<Event> {
let (tx, rx) = mpsc::channel();
let card = Self;
thread::spawn(move || loop {
let ctx = match reader::establish_context() {
Ok(c) => c,
Err(e) => {
let _ = tx.send(Event::Error(e.to_string()));
thread::sleep(Duration::from_secs(2));
continue;
}
};
let readers = match reader::list_readers(&ctx) {
Ok(r) => r,
Err(_) => {
thread::sleep(Duration::from_secs(2));
continue;
}
};
let idx = match reader::wait_for_card(&ctx, &readers) {
Ok(i) => i,
Err(e) => {
let _ = tx.send(Event::Error(e.to_string()));
continue;
}
};
let reader_name = readers[idx].clone();
let _ = tx.send(Event::CardInserted {
reader: reader_name.clone(),
});
match card.read_card(&ctx, &reader_name, &opts) {
Ok(data) => {
let _ = tx.send(Event::CardData(data));
}
Err(e) => {
let _ = tx.send(Event::Error(e.to_string()));
}
}
let _ = reader::wait_for_card_removal(&ctx, idx, &readers);
let _ = tx.send(Event::CardRemoved {
reader: reader_name,
});
});
rx
}
fn read_card(
&self,
ctx: &Context,
reader: &str,
opts: &Options,
) -> Result<model::CardData, CardError> {
let card = reader::connect_card(ctx, reader)?;
let status = card
.status2_owned()
.map_err(|e| CardError::Context(e.to_string()))?;
let resp_cmd = reader::get_response_command(status.atr());
let personal = personal::read_personal(&card, &resp_cmd, opts.show_face_image);
let card_info = if opts.show_laser_data {
Some(laser::read_laser_id(&card, &resp_cmd))
} else {
None
};
let nhso_data = if opts.show_nhso_data {
Some(nhso::read_nhso(&card, &resp_cmd))
} else {
None
};
drop(card);
Ok(model::CardData {
personal: Some(personal),
card: card_info,
nhso: nhso_data,
})
}
}
impl Default for SmartCard {
fn default() -> Self {
Self::new()
}
}