Skip to main content

pcsc/
lib.rs

1//! Communicate with smart cards using the PC/SC API.
2//!
3//! PC/SC (Personal Computer/Smart Card) is a standard API for
4//! communicating with smart cards -- enumerating card readers, connecting
5//! to smart cards, sending them commands, etc. See [Wikipedia][1] and
6//! [PC/SC Workgroup][2] for more information.
7//!
8//! [1]: https://en.wikipedia.org/wiki/PC/SC
9//! [2]: https://pcscworkgroup.com/
10//!
11//! This library is a safe and ergonomic FFI wrapper around the following
12//! PC/SC implementations:
13//!
14//! - On Windows, the built-in `WinSCard.dll` library and "Smart Card"
15//!   service. See [MSDN][3] for documentation of the implemented API.
16//!
17//! - On macOS, the built-in PCSC framework.
18//!
19//! - On Linux, BSDs and (hopefully) other systems, the PCSC lite library
20//!   and pcscd daemon. See [pcsclite][4] for documentation of the
21//!   implemented API.
22//!
23//! This crate depends on the [`pcsc-sys`][5] crate. See its documentation
24//! if you need to customize how the PCSC implementation is found.
25//!
26//! [3]: https://msdn.microsoft.com/EN-US/library/aa374731.aspx#smart_card_functions
27//! [4]: https://pcsclite.apdu.fr/
28//! [5]: https://docs.rs/pcsc-sys
29//!
30//! ## Communicating with a smart card
31//!
32//! To communicate with a smart card, you send it APDU (Application
33//! Protocol Data Unit) commands, and receive APDU responses.
34//!
35//! The format of these commands is described in the [ISO 7816 Part 4][6]
36//! standard. The commands themselves vary based on the application on the
37//! card.
38//!
39//! [6]: https://cardwerk.com/iso-7816-part-4/
40//!
41//! ## Note on portability
42//!
43//! The various implementations are not fully consistent with each other,
44//! and some may also miss various features or exhibit various bugs.
45//! Hence, you cannot assume that code which works on one platform will
46//! behave the same in all other platforms - unfortunately, some
47//! adjustments might be needed to reach a common base. See [pcsclite][4]
48//! for a list of documented differences, and [Ludovic Rousseau's blog][7]
49//! archive for many more details.
50//!
51//! [7]: https://ludovicrousseau.blogspot.com/
52//!
53//! Not all PC/SC functionality is covered yet; if you are missing
54//! something, please open an issue.
55//!
56//! ## Note on strings
57//!
58//! The library uses C strings (`&CStr`) for all strings (e.g. card reader
59//! names), to avoid any allocation and conversion overhead.
60//!
61//! In pcsclite and macOS, all strings are guaranteed to be UTF-8 encoded.
62//!
63//! In Windows, the API provides two variants of all functions dealing
64//! with strings - ASCII and Unicode (in this case, meaning 16-bits wide
65//! strings). For ease of implementation, this library wraps the ASCII
66//! variants only. (If you require Unicode names in Windows, please open
67//! an issue.)
68//!
69//! Since ASCII is a subset of UTF-8, you can thus safely use UTF-8
70//! conversion functions such as `to_str()` to obtain an `&str`/`String`
71//! from this library -- but don't do this if you don't need to ☺
72//!
73//! ## Note on thread safety and blocking operations
74//!
75//! A library context can be safely moved to another thread or cloned and
76//! used from multiple threads.
77//!
78//! Operations on a given context are not performed concurrently. If one
79//! thread performs a blocking operation on a context, such as
80//! `get_status_change()`, then another operation on the context will
81//! block until the ongoing operation finishes.
82//!
83//! An ongoing blocking operation on a context can be canceled from another
84//! thread by calling the `cancel` function on the context.
85//!
86//! If you want to perform concurrent operations, for example, monitor
87//! smart card reader changes in one thread, and send commands to cards in
88//! another, create a different context for each thread.
89//!
90//! Note however, that if one context has an exclusive transaction with a
91//! card, any operation on the same underlying card from not within the
92//! transaction will block even across contexts.
93//!
94//! When issuing a series of commands to a card, it is recommended to always
95//! use a transaction -- other programs and even system services can get in
96//! the way otherwise, even if you don't expect it.
97//!
98//! See [MSDN][8] for more details.
99//!
100//! [8]: https://msdn.microsoft.com/en-us/library/ms953432.aspx#smartcardcspcook_topic2
101#![allow(deprecated)]
102#![allow(clippy::bad_bit_mask)]
103
104use std::ffi::{CStr, CString};
105use std::mem::{forget, transmute};
106use std::ops::Deref;
107use std::os::raw::c_char;
108use std::ptr::{null, null_mut};
109use std::sync::Arc;
110
111use bitflags::bitflags;
112pub use pcsc_sys as ffi;
113
114use ffi::{DWORD, LONG};
115
116// We use these instead of std::mem::uninitialized -- variables which are
117// set to this are always overridden and the dummy values are never exposed.
118const DUMMY_LONG: LONG = -1;
119const DUMMY_DWORD: DWORD = 0xdead_beef;
120
121// Note on potentially problematic casts (clippy lints `cast-sign-loss`,
122// `cast-possible-truncation`): from my analysis they are all OK, for
123// both 32bit and 64bit DWORD/LONG. But it is sketchy.
124
125bitflags! {
126    /// A mask of the state a card reader.
127    // Derives to keep backward compat with bitflags 1.
128    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
129    pub struct State: DWORD {
130        const UNAWARE = ffi::SCARD_STATE_UNAWARE;
131        const IGNORE = ffi::SCARD_STATE_IGNORE;
132        const CHANGED = ffi::SCARD_STATE_CHANGED;
133        const UNKNOWN = ffi::SCARD_STATE_UNKNOWN;
134        const UNAVAILABLE = ffi::SCARD_STATE_UNAVAILABLE;
135        const EMPTY = ffi::SCARD_STATE_EMPTY;
136        const PRESENT = ffi::SCARD_STATE_PRESENT;
137        const ATRMATCH = ffi::SCARD_STATE_ATRMATCH;
138        const EXCLUSIVE = ffi::SCARD_STATE_EXCLUSIVE;
139        const INUSE = ffi::SCARD_STATE_INUSE;
140        const MUTE = ffi::SCARD_STATE_MUTE;
141        const UNPOWERED = ffi::SCARD_STATE_UNPOWERED;
142    }
143}
144
145// Backward compat with bitflags 1.
146impl State {
147    #[deprecated = "use the safe `from_bits_retain` method instead"]
148    pub unsafe fn from_bits_unchecked(bits: DWORD) -> Self {
149        Self::from_bits_retain(bits)
150    }
151}
152
153bitflags! {
154    /// A mask of the status of a card in a card reader.
155    ///
156    /// # Portability note
157    ///
158    /// On Windows, Status always has exactly one bit set, and the bit values do
159    /// not correspond to underlying PC/SC constants. This allows Status to be
160    /// used in the same way across all platforms.
161    // Derives to keep backward compat with bitflags 1.
162    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
163    pub struct Status: DWORD {
164        const UNKNOWN = {
165            #[cfg(not(target_os = "windows"))] { ffi::SCARD_UNKNOWN }
166            #[cfg(target_os = "windows")] { 0x0001 }
167        };
168        const ABSENT = {
169            #[cfg(not(target_os = "windows"))] { ffi::SCARD_ABSENT }
170            #[cfg(target_os = "windows")] { 0x0002 }
171        };
172        const PRESENT = {
173            #[cfg(not(target_os = "windows"))] { ffi::SCARD_PRESENT }
174            #[cfg(target_os = "windows")] { 0x0004 }
175        };
176        const SWALLOWED = {
177            #[cfg(not(target_os = "windows"))] { ffi::SCARD_SWALLOWED }
178            #[cfg(target_os = "windows")] { 0x0008 }
179        };
180        const POWERED = {
181            #[cfg(not(target_os = "windows"))] { ffi::SCARD_POWERED }
182            #[cfg(target_os = "windows")] { 0x0010 }
183        };
184        const NEGOTIABLE = {
185            #[cfg(not(target_os = "windows"))] { ffi::SCARD_NEGOTIABLE }
186            #[cfg(target_os = "windows")] { 0x0020 }
187        };
188        const SPECIFIC = {
189            #[cfg(not(target_os = "windows"))] { ffi::SCARD_SPECIFIC }
190            #[cfg(target_os = "windows")] { 0x0040 }
191        };
192    }
193}
194
195impl Status {
196    fn from_raw(raw_status: DWORD) -> Self {
197        #[cfg(target_os = "windows")]
198        match raw_status {
199            ffi::SCARD_UNKNOWN => Status::UNKNOWN,
200            ffi::SCARD_ABSENT => Status::ABSENT,
201            ffi::SCARD_PRESENT => Status::PRESENT,
202            ffi::SCARD_SWALLOWED => Status::SWALLOWED,
203            ffi::SCARD_POWERED => Status::POWERED,
204            ffi::SCARD_NEGOTIABLE => Status::NEGOTIABLE,
205            ffi::SCARD_SPECIFIC => Status::SPECIFIC,
206            _ => Status::empty(),
207        }
208
209        #[cfg(not(target_os = "windows"))]
210        Status::from_bits_truncate(raw_status)
211    }
212
213    // Backward compat with bitflags 1.
214    #[deprecated = "use the safe `from_bits_retain` method instead"]
215    pub unsafe fn from_bits_unchecked(bits: DWORD) -> Self {
216        Self::from_bits_retain(bits)
217    }
218}
219
220/// How a reader connection is shared.
221#[repr(u32)]
222#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
223pub enum ShareMode {
224    Exclusive = ffi::SCARD_SHARE_EXCLUSIVE as u32,
225    Shared = ffi::SCARD_SHARE_SHARED as u32,
226    Direct = ffi::SCARD_SHARE_DIRECT as u32,
227}
228
229impl ShareMode {
230    fn into_raw(self) -> DWORD {
231        DWORD::from(self as u32)
232    }
233}
234
235/// A smart card communication protocol.
236#[repr(u32)]
237#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
238pub enum Protocol {
239    T0 = ffi::SCARD_PROTOCOL_T0 as u32,
240    T1 = ffi::SCARD_PROTOCOL_T1 as u32,
241    RAW = ffi::SCARD_PROTOCOL_RAW as u32,
242}
243
244impl Protocol {
245    fn from_raw(raw: DWORD) -> Option<Protocol> {
246        match raw {
247            ffi::SCARD_PROTOCOL_UNDEFINED => None,
248            ffi::SCARD_PROTOCOL_T0 => Some(Protocol::T0),
249            ffi::SCARD_PROTOCOL_T1 => Some(Protocol::T1),
250            ffi::SCARD_PROTOCOL_RAW => Some(Protocol::RAW),
251            // This should not be possible, since we only allow to select
252            // from Protocol's variants (or none).
253            _ => panic!("impossible protocol: {:#x}", raw),
254        }
255    }
256}
257
258bitflags! {
259    /// A mask of possible communication protocols.
260    // Derives to keep backward compat with bitflags 1.
261    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
262    pub struct Protocols: DWORD {
263        const UNDEFINED = ffi::SCARD_PROTOCOL_UNDEFINED;
264        const T0 = ffi::SCARD_PROTOCOL_T0;
265        const T1 = ffi::SCARD_PROTOCOL_T1;
266        const RAW = ffi::SCARD_PROTOCOL_RAW;
267        const ANY = ffi::SCARD_PROTOCOL_ANY;
268    }
269}
270
271// Backward compat with bitflags 1.
272impl Protocols {
273    #[deprecated = "use the safe `from_bits_retain` method instead"]
274    pub unsafe fn from_bits_unchecked(bits: DWORD) -> Self {
275        Self::from_bits_retain(bits)
276    }
277}
278
279/// Disposition method when disconnecting from a card reader.
280#[repr(u32)]
281#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
282pub enum Disposition {
283    LeaveCard = ffi::SCARD_LEAVE_CARD as u32,
284    ResetCard = ffi::SCARD_RESET_CARD as u32,
285    UnpowerCard = ffi::SCARD_UNPOWER_CARD as u32,
286    EjectCard = ffi::SCARD_EJECT_CARD as u32,
287}
288
289impl Disposition {
290    fn into_raw(self) -> DWORD {
291        DWORD::from(self as u32)
292    }
293}
294
295/// Possible library errors.
296///
297/// See [pcsclite][1], [MSDN][2].
298///
299/// [1]: https://pcsclite.apdu.fr/api/group__ErrorCodes.html
300/// [2]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa374738(v=vs.85).aspx#smart_card_return_values
301#[repr(u32)]
302#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
303pub enum Error {
304    // <contiguous block 1>
305    InternalError = ffi::SCARD_F_INTERNAL_ERROR as u32,
306    Cancelled = ffi::SCARD_E_CANCELLED as u32,
307    InvalidHandle = ffi::SCARD_E_INVALID_HANDLE as u32,
308    InvalidParameter = ffi::SCARD_E_INVALID_PARAMETER as u32,
309    InvalidTarget = ffi::SCARD_E_INVALID_TARGET as u32,
310    NoMemory = ffi::SCARD_E_NO_MEMORY as u32,
311    WaitedTooLong = ffi::SCARD_F_WAITED_TOO_LONG as u32,
312    InsufficientBuffer = ffi::SCARD_E_INSUFFICIENT_BUFFER as u32,
313    UnknownReader = ffi::SCARD_E_UNKNOWN_READER as u32,
314    Timeout = ffi::SCARD_E_TIMEOUT as u32,
315    SharingViolation = ffi::SCARD_E_SHARING_VIOLATION as u32,
316    NoSmartcard = ffi::SCARD_E_NO_SMARTCARD as u32,
317    UnknownCard = ffi::SCARD_E_UNKNOWN_CARD as u32,
318    CantDispose = ffi::SCARD_E_CANT_DISPOSE as u32,
319    ProtoMismatch = ffi::SCARD_E_PROTO_MISMATCH as u32,
320    NotReady = ffi::SCARD_E_NOT_READY as u32,
321    InvalidValue = ffi::SCARD_E_INVALID_VALUE as u32,
322    SystemCancelled = ffi::SCARD_E_SYSTEM_CANCELLED as u32,
323    CommError = ffi::SCARD_F_COMM_ERROR as u32,
324    UnknownError = ffi::SCARD_F_UNKNOWN_ERROR as u32,
325    InvalidAtr = ffi::SCARD_E_INVALID_ATR as u32,
326    NotTransacted = ffi::SCARD_E_NOT_TRANSACTED as u32,
327    ReaderUnavailable = ffi::SCARD_E_READER_UNAVAILABLE as u32,
328    Shutdown = ffi::SCARD_P_SHUTDOWN as u32,
329    PciTooSmall = ffi::SCARD_E_PCI_TOO_SMALL as u32,
330    ReaderUnsupported = ffi::SCARD_E_READER_UNSUPPORTED as u32,
331    DuplicateReader = ffi::SCARD_E_DUPLICATE_READER as u32,
332    CardUnsupported = ffi::SCARD_E_CARD_UNSUPPORTED as u32,
333    NoService = ffi::SCARD_E_NO_SERVICE as u32,
334    ServiceStopped = ffi::SCARD_E_SERVICE_STOPPED as u32,
335    #[cfg(target_os = "windows")]
336    Unexpected = ffi::SCARD_E_UNEXPECTED as u32,
337    IccInstallation = ffi::SCARD_E_ICC_INSTALLATION as u32,
338    IccCreateorder = ffi::SCARD_E_ICC_CREATEORDER as u32,
339    UnsupportedFeature = ffi::SCARD_E_UNSUPPORTED_FEATURE as u32,
340    DirNotFound = ffi::SCARD_E_DIR_NOT_FOUND as u32,
341    FileNotFound = ffi::SCARD_E_FILE_NOT_FOUND as u32,
342    NoDir = ffi::SCARD_E_NO_DIR as u32,
343    NoFile = ffi::SCARD_E_NO_FILE as u32,
344    NoAccess = ffi::SCARD_E_NO_ACCESS as u32,
345    WriteTooMany = ffi::SCARD_E_WRITE_TOO_MANY as u32,
346    BadSeek = ffi::SCARD_E_BAD_SEEK as u32,
347    InvalidChv = ffi::SCARD_E_INVALID_CHV as u32,
348    UnknownResMng = ffi::SCARD_E_UNKNOWN_RES_MNG as u32,
349    NoSuchCertificate = ffi::SCARD_E_NO_SUCH_CERTIFICATE as u32,
350    CertificateUnavailable = ffi::SCARD_E_CERTIFICATE_UNAVAILABLE as u32,
351    NoReadersAvailable = ffi::SCARD_E_NO_READERS_AVAILABLE as u32,
352    CommDataLost = ffi::SCARD_E_COMM_DATA_LOST as u32,
353    NoKeyContainer = ffi::SCARD_E_NO_KEY_CONTAINER as u32,
354    ServerTooBusy = ffi::SCARD_E_SERVER_TOO_BUSY as u32,
355    // </contiguous block 1>
356
357    // <contiguous block 2>
358    UnsupportedCard = ffi::SCARD_W_UNSUPPORTED_CARD as u32,
359    UnresponsiveCard = ffi::SCARD_W_UNRESPONSIVE_CARD as u32,
360    UnpoweredCard = ffi::SCARD_W_UNPOWERED_CARD as u32,
361    ResetCard = ffi::SCARD_W_RESET_CARD as u32,
362    RemovedCard = ffi::SCARD_W_REMOVED_CARD as u32,
363
364    SecurityViolation = ffi::SCARD_W_SECURITY_VIOLATION as u32,
365    WrongChv = ffi::SCARD_W_WRONG_CHV as u32,
366    ChvBlocked = ffi::SCARD_W_CHV_BLOCKED as u32,
367    Eof = ffi::SCARD_W_EOF as u32,
368    CancelledByUser = ffi::SCARD_W_CANCELLED_BY_USER as u32,
369    CardNotAuthenticated = ffi::SCARD_W_CARD_NOT_AUTHENTICATED as u32,
370
371    CacheItemNotFound = ffi::SCARD_W_CACHE_ITEM_NOT_FOUND as u32,
372    CacheItemStale = ffi::SCARD_W_CACHE_ITEM_STALE as u32,
373    CacheItemTooBig = ffi::SCARD_W_CACHE_ITEM_TOO_BIG as u32,
374    // </contiguous block 2>
375}
376
377impl Error {
378    #[allow(clippy::manual_range_contains)]
379    fn from_raw(raw: LONG) -> Error {
380        unsafe {
381            // The ranges here are the "blocks" above.
382            if ffi::SCARD_F_INTERNAL_ERROR <= raw && raw <= ffi::SCARD_E_SERVER_TOO_BUSY
383                || ffi::SCARD_W_UNSUPPORTED_CARD <= raw && raw <= ffi::SCARD_W_CACHE_ITEM_TOO_BIG
384            {
385                transmute::<u32, Error>(raw as u32)
386            } else {
387                if cfg!(debug_assertions) {
388                    panic!("unknown PCSC error code: {:#x}", raw);
389                }
390                // We mask unknown error codes here; this is not very nice,
391                // but seems better than panicking.
392                Error::UnknownError
393            }
394        }
395    }
396
397    fn into_raw(self) -> LONG {
398        // Note: not using LONG::from() - won't work when LONG is i32.
399        self as u32 as LONG
400    }
401}
402
403impl std::error::Error for Error {
404    fn description(&self) -> &str {
405        // The descriptions are from MSDN.
406        match *self {
407            Error::InternalError => "An internal consistency check failed",
408            Error::Cancelled => "The action was cancelled by an SCardCancel request",
409            Error::InvalidHandle => "The supplied handle was invalid",
410            Error::InvalidParameter => "One or more of the supplied parameters could not be properly interpreted",
411            Error::InvalidTarget => "Registry startup information is missing or invalid",
412            Error::NoMemory => "Not enough memory available to complete this command",
413            Error::WaitedTooLong => "An internal consistency timer has expired",
414            Error::InsufficientBuffer => "The data buffer to receive returned data is too small for the returned data",
415            Error::UnknownReader => "The specified reader name is not recognized",
416            Error::Timeout => "The user-specified timeout value has expired",
417            Error::SharingViolation => "The smart card cannot be accessed because of other connections outstanding",
418            Error::NoSmartcard => "The operation requires a Smart Card, but no Smart Card is currently in the device",
419            Error::UnknownCard => "The specified smart card name is not recognized",
420            Error::CantDispose => "The system could not dispose of the media in the requested manner",
421            Error::ProtoMismatch => {
422                "The requested protocols are incompatible with the protocol currently in use with the smart card"
423            }
424            Error::NotReady => "The reader or smart card is not ready to accept commands",
425            Error::InvalidValue => "One or more of the supplied parameters values could not be properly interpreted",
426            Error::SystemCancelled => "The action was cancelled by the system, presumably to log off or shut down",
427            Error::CommError => "An internal communications error has been detected",
428            Error::UnknownError => "An internal error has been detected, but the source is unknown",
429            Error::InvalidAtr => "An ATR obtained from the registry is not a valid ATR string",
430            Error::NotTransacted => "An attempt was made to end a non-existent transaction",
431            Error::ReaderUnavailable => "The specified reader is not currently available for use",
432            Error::Shutdown => "The operation has been aborted to allow the server application to exit",
433            Error::PciTooSmall => "The PCI Receive buffer was too small",
434            Error::ReaderUnsupported => "The reader driver does not meet minimal requirements for support",
435            Error::DuplicateReader => "The reader driver did not produce a unique reader name",
436            Error::CardUnsupported => "The smart card does not meet minimal requirements for support",
437            Error::NoService => "The Smart card resource manager is not running",
438            Error::ServiceStopped => "The Smart card resource manager has shut down",
439            #[cfg(target_os = "windows")]
440            Error::Unexpected => "An unexpected card error has occurred",
441            Error::UnsupportedFeature => "This smart card does not support the requested feature",
442            Error::IccInstallation => "No primary provider can be found for the smart card",
443            Error::IccCreateorder => "The requested order of object creation is not supported",
444            Error::DirNotFound => "The identified directory does not exist in the smart card",
445            Error::FileNotFound => "The identified file does not exist in the smart card",
446            Error::NoDir => "The supplied path does not represent a smart card directory",
447            Error::NoFile => "The supplied path does not represent a smart card file",
448            Error::NoAccess => "Access is denied to this file",
449            Error::WriteTooMany => "The smart card does not have enough memory to store the information",
450            Error::BadSeek => "There was an error trying to set the smart card file object pointer",
451            Error::InvalidChv => "The supplied PIN is incorrect",
452            Error::UnknownResMng => "An unrecognized error code was returned from a layered component",
453            Error::NoSuchCertificate => "The requested certificate does not exist",
454            Error::CertificateUnavailable => "The requested certificate could not be obtained",
455            Error::NoReadersAvailable => "Cannot find a smart card reader",
456            Error::CommDataLost => "A communications error with the smart card has been detected. Retry the operation",
457            Error::NoKeyContainer => "The requested key container does not exist on the smart card",
458            Error::ServerTooBusy => "The smart card resource manager is too busy to complete this operation",
459            Error::UnsupportedCard => {
460                "The reader cannot communicate with the card, due to ATR string configuration conflicts"
461            }
462            Error::UnresponsiveCard => "The smart card is not responding to a reset",
463            Error::UnpoweredCard => {
464                "Power has been removed from the smart card, so that further communication is not possible"
465            }
466            Error::ResetCard => "The smart card has been reset, so any shared state information is invalid",
467            Error::RemovedCard => "The smart card has been removed, so further communication is not possible",
468            Error::SecurityViolation => "Access was denied because of a security violation",
469            Error::WrongChv => "The card cannot be accessed because the wrong PIN was presented",
470            Error::ChvBlocked => {
471                "The card cannot be accessed because the maximum number of PIN entry attempts has been reached"
472            }
473            Error::Eof => "The end of the smart card file has been reached",
474            Error::CancelledByUser => r#"The user pressed "Cancel" on a Smart Card Selection Dialog"#,
475            Error::CardNotAuthenticated => "No PIN was presented to the smart card",
476            Error::CacheItemNotFound => "The requested item could not be found in the cache",
477            Error::CacheItemStale => "The requested cache item is too old and was deleted from the cache",
478            Error::CacheItemTooBig => "The new cache item exceeds the maximum per-item size defined for the cache",
479        }
480    }
481}
482
483impl std::fmt::Display for Error {
484    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
485        f.write_str(std::error::Error::description(self))
486    }
487}
488
489macro_rules! try_pcsc {
490    ($e:expr) => {
491        match $e {
492            ffi::SCARD_S_SUCCESS => (),
493            err => return Err(Error::from_raw(err)),
494        }
495    };
496}
497
498/// Scope of a context.
499#[repr(u32)]
500#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
501pub enum Scope {
502    User = ffi::SCARD_SCOPE_USER as u32,
503    Terminal = ffi::SCARD_SCOPE_TERMINAL as u32,
504    System = ffi::SCARD_SCOPE_SYSTEM as u32,
505    Global = ffi::SCARD_SCOPE_GLOBAL as u32,
506}
507
508impl Scope {
509    fn into_raw(self) -> DWORD {
510        DWORD::from(self as u32)
511    }
512}
513
514/// A class of Attributes.
515#[repr(u32)]
516#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
517pub enum AttributeClass {
518    VendorInfo = ffi::SCARD_CLASS_VENDOR_INFO as u32,
519    Communications = ffi::SCARD_CLASS_COMMUNICATIONS as u32,
520    Protocol = ffi::SCARD_CLASS_PROTOCOL as u32,
521    PowerMgmt = ffi::SCARD_CLASS_POWER_MGMT as u32,
522    Security = ffi::SCARD_CLASS_SECURITY as u32,
523    Mechanical = ffi::SCARD_CLASS_MECHANICAL as u32,
524    VendorDefined = ffi::SCARD_CLASS_VENDOR_DEFINED as u32,
525    IfdProtocol = ffi::SCARD_CLASS_IFD_PROTOCOL as u32,
526    IccState = ffi::SCARD_CLASS_ICC_STATE as u32,
527    System = ffi::SCARD_CLASS_SYSTEM as u32,
528}
529
530/// Card reader attribute types.
531#[repr(u32)]
532#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
533pub enum Attribute {
534    VendorName = ffi::SCARD_ATTR_VENDOR_NAME as u32,
535    VendorIfdType = ffi::SCARD_ATTR_VENDOR_IFD_TYPE as u32,
536    VendorIfdVersion = ffi::SCARD_ATTR_VENDOR_IFD_VERSION as u32,
537    VendorIfdSerialNo = ffi::SCARD_ATTR_VENDOR_IFD_SERIAL_NO as u32,
538    ChannelId = ffi::SCARD_ATTR_CHANNEL_ID as u32,
539    AsyncProtocolTypes = ffi::SCARD_ATTR_ASYNC_PROTOCOL_TYPES as u32,
540    DefaultClk = ffi::SCARD_ATTR_DEFAULT_CLK as u32,
541    MaxClk = ffi::SCARD_ATTR_MAX_CLK as u32,
542    DefaultDataRate = ffi::SCARD_ATTR_DEFAULT_DATA_RATE as u32,
543    MaxDataRate = ffi::SCARD_ATTR_MAX_DATA_RATE as u32,
544    MaxIfsd = ffi::SCARD_ATTR_MAX_IFSD as u32,
545    SyncProtocolTypes = ffi::SCARD_ATTR_SYNC_PROTOCOL_TYPES as u32,
546    PowerMgmtSupport = ffi::SCARD_ATTR_POWER_MGMT_SUPPORT as u32,
547    UserToCardAuthDevice = ffi::SCARD_ATTR_USER_TO_CARD_AUTH_DEVICE as u32,
548    UserAuthInputDevice = ffi::SCARD_ATTR_USER_AUTH_INPUT_DEVICE as u32,
549    Characteristics = ffi::SCARD_ATTR_CHARACTERISTICS as u32,
550
551    CurrentProtocolType = ffi::SCARD_ATTR_CURRENT_PROTOCOL_TYPE as u32,
552    CurrentClk = ffi::SCARD_ATTR_CURRENT_CLK as u32,
553    CurrentF = ffi::SCARD_ATTR_CURRENT_F as u32,
554    CurrentD = ffi::SCARD_ATTR_CURRENT_D as u32,
555    CurrentN = ffi::SCARD_ATTR_CURRENT_N as u32,
556    CurrentW = ffi::SCARD_ATTR_CURRENT_W as u32,
557    CurrentIfsc = ffi::SCARD_ATTR_CURRENT_IFSC as u32,
558    CurrentIfsd = ffi::SCARD_ATTR_CURRENT_IFSD as u32,
559    CurrentBwt = ffi::SCARD_ATTR_CURRENT_BWT as u32,
560    CurrentCwt = ffi::SCARD_ATTR_CURRENT_CWT as u32,
561    CurrentEbcEncoding = ffi::SCARD_ATTR_CURRENT_EBC_ENCODING as u32,
562    ExtendedBwt = ffi::SCARD_ATTR_EXTENDED_BWT as u32,
563
564    IccPresence = ffi::SCARD_ATTR_ICC_PRESENCE as u32,
565    IccInterfaceStatus = ffi::SCARD_ATTR_ICC_INTERFACE_STATUS as u32,
566    CurrentIoState = ffi::SCARD_ATTR_CURRENT_IO_STATE as u32,
567    AtrString = ffi::SCARD_ATTR_ATR_STRING as u32,
568    IccTypePerAtr = ffi::SCARD_ATTR_ICC_TYPE_PER_ATR as u32,
569
570    EscReset = ffi::SCARD_ATTR_ESC_RESET as u32,
571    EscCancel = ffi::SCARD_ATTR_ESC_CANCEL as u32,
572    EscAuthrequest = ffi::SCARD_ATTR_ESC_AUTHREQUEST as u32,
573    Maxinput = ffi::SCARD_ATTR_MAXINPUT as u32,
574
575    DeviceUnit = ffi::SCARD_ATTR_DEVICE_UNIT as u32,
576    DeviceInUse = ffi::SCARD_ATTR_DEVICE_IN_USE as u32,
577    DeviceFriendlyName = ffi::SCARD_ATTR_DEVICE_FRIENDLY_NAME as u32,
578    DeviceSystemName = ffi::SCARD_ATTR_DEVICE_SYSTEM_NAME as u32,
579    SupressT1IfsRequest = ffi::SCARD_ATTR_SUPRESS_T1_IFS_REQUEST as u32,
580}
581
582impl Attribute {
583    fn into_raw(self) -> DWORD {
584        DWORD::from(self as u32)
585    }
586}
587
588/// Maximum amount of bytes in an ATR.
589pub const MAX_ATR_SIZE: usize = ffi::MAX_ATR_SIZE;
590/// Maximum amount of bytes in a short APDU command or response.
591pub const MAX_BUFFER_SIZE: usize = ffi::MAX_BUFFER_SIZE;
592/// Maximum amount of bytes in an extended APDU command or response.
593pub const MAX_BUFFER_SIZE_EXTENDED: usize = ffi::MAX_BUFFER_SIZE_EXTENDED;
594
595/// A special reader name for detecting card reader insertions and removals.
596///
597/// # Note
598///
599/// This function is a wrapper around a constant, and is intended to be
600/// used as such.
601#[allow(non_snake_case)]
602// We can't have a const &CStr yet, so we simulate it with a function.
603pub fn PNP_NOTIFICATION() -> &'static CStr {
604    // The panic can't happen, but we avoid unsafe.
605    CStr::from_bytes_with_nul(b"\\\\?PnP?\\Notification\0").unwrap()
606}
607
608/// Transform a control code in the form expected by the platform.
609///
610/// Control codes passed to `Card::control` are usually defined as inputs
611/// to this function.
612///
613/// This function wraps the `SCARD_CTL_CODE` macro.
614pub const fn ctl_code(code: DWORD) -> DWORD {
615    ffi::SCARD_CTL_CODE(code)
616}
617
618/// A structure for tracking the current state of card readers and cards.
619///
620/// This structure wraps `SCARD_READERSTATE` ([pcsclite][1], [MSDN][2]).
621///
622/// [1]: https://pcsclite.apdu.fr/api/group__API.html#ga33247d5d1257d59e55647c3bb717db24
623/// [2]: https://msdn.microsoft.com/en-us/library/aa379808.aspx
624#[repr(C)]
625pub struct ReaderState {
626    // Note: must be directly transmutable to SCARD_READERSTATE.
627    inner: ffi::SCARD_READERSTATE,
628}
629
630// For some reason, linking in windows fails if we put these directly
631// in statics. This is why we have this function instead of the
632// SCARD_PCI_* defines from the C API.
633fn get_protocol_pci(protocol: Protocol) -> &'static ffi::SCARD_IO_REQUEST {
634    unsafe {
635        match protocol {
636            Protocol::T0 => &ffi::g_rgSCardT0Pci,
637            Protocol::T1 => &ffi::g_rgSCardT1Pci,
638            Protocol::RAW => &ffi::g_rgSCardRawPci,
639        }
640    }
641}
642
643struct ContextInner {
644    handle: ffi::SCARDCONTEXT,
645}
646
647/// Library context to the PCSC service.
648///
649/// This structure wraps `SCARDCONTEXT`.
650pub struct Context {
651    inner: Arc<ContextInner>,
652}
653
654/// A connection to a smart card.
655///
656/// This structure wraps `SCARDHANDLE`.
657pub struct Card {
658    // Keeps the context alive.
659    _context: Context,
660    handle: ffi::SCARDHANDLE,
661    active_protocol: Option<Protocol>,
662}
663
664/// An exclusive transaction with a card.
665///
666/// A transaction ensures uninterrupted access to the card for its
667/// duration. All other operations performed on the same underlying
668/// card (even from other processes) will block until the transaction
669/// is finished.
670// By taking a mut reference to the card we statically enforce that:
671// - There can only be one active transaction at a time.
672// - All operations on the card must be performed through the transaction
673//   for the duration of the transaction's lifetime.
674pub struct Transaction<'tx> {
675    card: &'tx mut Card,
676}
677
678/// An iterator over card reader names.
679///
680/// The iterator does not perform any copying or allocations; this is left
681/// to the caller's discretion. It is therefore tied to the underlying
682/// buffer.
683#[derive(Clone, Debug)]
684pub struct ReaderNames<'buf> {
685    buf: &'buf [u8],
686    pos: usize,
687}
688
689impl<'buf> Iterator for ReaderNames<'buf> {
690    type Item = &'buf CStr;
691
692    fn next(&mut self) -> Option<&'buf CStr> {
693        match self.buf[self.pos..].iter().position(|&c| c == 0) {
694            None | Some(0) => None,
695            Some(len) => {
696                let old_pos = self.pos;
697                self.pos += len + 1;
698                // The panic can't happen, but we avoid unsafe.
699                Some(CStr::from_bytes_with_nul(&self.buf[old_pos..self.pos]).unwrap())
700            }
701        }
702    }
703}
704
705impl Context {
706    /// Establish a new context.
707    ///
708    /// This function wraps `SCardEstablishContext` ([pcsclite][1],
709    /// [MSDN][2]).
710    ///
711    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#gaa1b8970169fd4883a6dc4a8f43f19b67
712    /// [2]: https://msdn.microsoft.com/en-us/library/aa379479.aspx
713    pub fn establish(scope: Scope) -> Result<Context, Error> {
714        unsafe {
715            let mut handle: ffi::SCARDCONTEXT = DUMMY_LONG as ffi::SCARDCONTEXT;
716
717            try_pcsc!(ffi::SCardEstablishContext(
718                scope.into_raw(),
719                null(),
720                null(),
721                &mut handle,
722            ));
723
724            Ok(Context {
725                inner: Arc::new(ContextInner { handle }),
726            })
727        }
728    }
729
730    /// Release the context.
731    ///
732    /// In case of error, ownership of the context is returned to the
733    /// caller.
734    ///
735    /// This function wraps `SCardReleaseContext` ([pcsclite][1],
736    /// [MSDN][2]).
737    ///
738    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#ga6aabcba7744c5c9419fdd6404f73a934
739    /// [2]: https://msdn.microsoft.com/en-us/library/aa379798.aspx
740    ///
741    /// ## Note
742    ///
743    /// `Context` implements `Drop` which automatically releases the
744    /// context; you only need to call this function if you want to handle
745    /// errors.
746    ///
747    /// If the `Context` was cloned, and a clone is still alive, this
748    /// function will fail with `Error::CantDispose`.
749    pub fn release(self) -> Result<(), (Context, Error)> {
750        match Arc::try_unwrap(self.inner) {
751            Ok(inner) => {
752                unsafe {
753                    let err = ffi::SCardReleaseContext(inner.handle);
754                    if err != ffi::SCARD_S_SUCCESS {
755                        let context = Context { inner: Arc::new(inner) };
756                        return Err((context, Error::from_raw(err)));
757                    }
758
759                    // Skip the drop, we did it "manually".
760                    forget(inner);
761
762                    Ok(())
763                }
764            }
765            Err(arc_inner) => {
766                let context = Context { inner: arc_inner };
767                Err((context, Error::CantDispose))
768            }
769        }
770    }
771
772    /// Check whether the Context is still valid.
773    ///
774    /// This function wraps `SCardIsValidContext` ([pcsclite][1],
775    /// [MSDN][2]).
776    ///
777    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#ga722eb66bcc44d391f700ff9065cc080b
778    /// [2]: https://msdn.microsoft.com/en-us/library/aa379788.aspx
779    pub fn is_valid(&self) -> Result<(), Error> {
780        unsafe {
781            try_pcsc!(ffi::SCardIsValidContext(self.inner.handle,));
782
783            Ok(())
784        }
785    }
786
787    /// Cancel any ongoing blocking operation in the Context.
788    ///
789    /// See the `cancel.rs` example program.
790    ///
791    /// This function wraps `SCardCancel` ([pcsclite][1], [MSDN][2]).
792    ///
793    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#gaacbbc0c6d6c0cbbeb4f4debf6fbeeee6
794    /// [2]: https://msdn.microsoft.com/en-us/library/aa379470.aspx
795    pub fn cancel(&self) -> Result<(), Error> {
796        unsafe {
797            try_pcsc!(ffi::SCardCancel(self.inner.handle,));
798
799            Ok(())
800        }
801    }
802
803    /// List all connected card readers.
804    ///
805    /// `buffer` is a buffer that should be large enough to hold all of
806    /// the connected reader names. The function `list_readers_len` can be
807    /// used to find the exact required length.
808    ///
809    /// Returns an iterator over the reader names. The iterator yields
810    /// values directly from `buffer`.
811    ///
812    /// If the buffer is not large enough to hold all of the names,
813    /// `Error::InsufficientBuffer` is returned.
814    ///
815    /// This function wraps `SCardListReaders` ([pcsclite][1], [MSDN][2]).
816    ///
817    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#ga93b07815789b3cf2629d439ecf20f0d9
818    /// [2]: https://msdn.microsoft.com/en-us/library/aa379793.aspx
819    pub fn list_readers<'buf>(&self, buffer: &'buf mut [u8]) -> Result<ReaderNames<'buf>, Error> {
820        unsafe {
821            assert!(buffer.len() <= std::u32::MAX as usize);
822            let mut buflen = buffer.len() as DWORD;
823            // SCardListReaders treats null specially, to query the needed
824            // buffer length. We don't want the caller to be able to trigger
825            // this here -- should use `list_readers_len` instead. So needs
826            // some special treatment.
827            let bufptr = if buflen == 0 {
828                null_mut()
829            } else {
830                let ptr = buffer.as_mut_ptr() as *mut c_char;
831                assert!(!ptr.is_null());
832                ptr
833            };
834
835            let err = ffi::SCardListReaders(self.inner.handle, null(), bufptr, &mut buflen);
836            if err == Error::NoReadersAvailable.into_raw() {
837                return Ok(ReaderNames { buf: b"\0", pos: 0 });
838            }
839            if err != ffi::SCARD_S_SUCCESS {
840                return Err(Error::from_raw(err));
841            }
842            if bufptr.is_null() {
843                return Err(Error::InsufficientBuffer);
844            }
845
846            Ok(ReaderNames {
847                buf: &buffer[..buflen as usize],
848                pos: 0,
849            })
850        }
851    }
852
853    /// Get the needed length of a buffer to be passed to `list_readers`.
854    ///
855    /// This function wraps `SCardListReaders` ([pcsclite][1], [MSDN][2]).
856    ///
857    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#ga93b07815789b3cf2629d439ecf20f0d9
858    /// [2]: https://msdn.microsoft.com/en-us/library/aa379793.aspx
859    pub fn list_readers_len(&self) -> Result<usize, Error> {
860        unsafe {
861            let mut buflen = DUMMY_DWORD;
862
863            let err = ffi::SCardListReaders(self.inner.handle, null(), null_mut(), &mut buflen);
864            if err == Error::NoReadersAvailable.into_raw() {
865                return Ok(0);
866            }
867            if err != ffi::SCARD_S_SUCCESS {
868                return Err(Error::from_raw(err));
869            }
870
871            Ok(buflen as usize)
872        }
873    }
874
875    /// List all connected card readers, allocating buffers of the required size.
876    ///
877    /// This function wraps `SCardListReaders` ([pcsclite][1], [MSDN][2]).  It is an owned version
878    /// of [`list_readers`](#method.list_readers), calling
879    /// [`list_readers_len`](#method.list_readers_len) to determine the required buffer size.
880    ///
881    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#ga93b07815789b3cf2629d439ecf20f0d9
882    /// [2]: https://msdn.microsoft.com/en-us/library/aa379793.aspx
883    pub fn list_readers_owned(&self) -> Result<Vec<CString>, Error> {
884        let len = self.list_readers_len()?;
885        if len == 0 {
886            return Ok(vec![]);
887        }
888        let mut buffer = vec![0u8; len];
889        Ok(self.list_readers(&mut buffer)?.map(ToOwned::to_owned).collect())
890    }
891
892    /// Connect to a card which is present in a reader.
893    ///
894    /// See the `connect.rs` example program.
895    ///
896    /// This function wraps `SCardConnect` ([pcsclite][1], [MSDN][2]).
897    ///
898    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#ga4e515829752e0a8dbc4d630696a8d6a5
899    /// [2]: https://msdn.microsoft.com/en-us/library/aa379473.aspx
900    pub fn connect(&self, reader: &CStr, share_mode: ShareMode, preferred_protocols: Protocols) -> Result<Card, Error> {
901        unsafe {
902            let mut handle: ffi::SCARDHANDLE = DUMMY_LONG as ffi::SCARDHANDLE;
903            let mut raw_active_protocol: DWORD = DUMMY_DWORD;
904
905            try_pcsc!(ffi::SCardConnect(
906                self.inner.handle,
907                reader.as_ptr(),
908                share_mode.into_raw(),
909                preferred_protocols.bits(),
910                &mut handle,
911                &mut raw_active_protocol,
912            ));
913
914            let active_protocol = Protocol::from_raw(raw_active_protocol);
915
916            Ok(Card {
917                _context: self.clone(),
918                handle,
919                active_protocol,
920            })
921        }
922    }
923
924    /// Wait for card and card reader state changes.
925    ///
926    /// The function blocks until the state of one of the readers changes
927    /// from corresponding passed-in `ReaderState`. The `ReaderState`s are
928    /// updated to report the new state.
929    ///
930    /// A special reader name, `\\?PnP?\Notification`, can be used to
931    /// detect card reader insertions and removals, as opposed to state
932    /// changes of a specific reader. Use `PNP_NOTIFICATION()` to easily
933    /// obtain a static reference to this name.
934    ///
935    /// See the `monitor.rs` example program.
936    ///
937    /// This function wraps `SCardGetStatusChange` ([pcsclite][1],
938    /// [MSDN][2]).
939    ///
940    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#ga33247d5d1257d59e55647c3bb717db24
941    /// [2]: https://msdn.microsoft.com/en-us/library/aa379773.aspx
942    pub fn get_status_change<D>(&self, timeout: D, readers: &mut [ReaderState]) -> Result<(), Error>
943    where
944        D: Into<Option<std::time::Duration>>,
945    {
946        let timeout_ms = match timeout.into() {
947            Some(duration) => {
948                let timeout_ms_u64 = duration
949                    .as_secs()
950                    .saturating_mul(1000)
951                    .saturating_add(u64::from(duration.subsec_nanos()) / 1_000_000);
952                std::cmp::min(ffi::INFINITE, timeout_ms_u64 as DWORD)
953            }
954            None => ffi::INFINITE,
955        };
956
957        unsafe {
958            assert!(readers.len() <= std::u32::MAX as usize);
959
960            try_pcsc!(ffi::SCardGetStatusChange(
961                self.inner.handle,
962                timeout_ms,
963                readers.as_mut_ptr() as *mut ffi::SCARD_READERSTATE,
964                readers.len() as DWORD,
965            ));
966
967            Ok(())
968        }
969    }
970}
971
972impl Drop for ContextInner {
973    fn drop(&mut self) {
974        unsafe {
975            // Error is ignored here; to do proper error handling,
976            // release() should be called manually.
977            let _err = ffi::SCardReleaseContext(self.handle);
978        }
979    }
980}
981
982impl Clone for Context {
983    /// Clone the `Context`.
984    ///
985    /// ## Implementation note
986    ///
987    /// This is implemented in the Rust side with an `Arc::clone`.
988    fn clone(&self) -> Self {
989        Context {
990            inner: Arc::clone(&self.inner),
991        }
992    }
993}
994
995unsafe impl Send for Context {}
996unsafe impl Sync for Context {}
997
998impl ReaderState {
999    /// Create a ReaderState for a card reader with a given presumed
1000    /// state.
1001    pub fn new<T: Into<CString>>(name: T, current_state: State) -> ReaderState {
1002        ReaderState {
1003            inner: ffi::SCARD_READERSTATE {
1004                szReader: name.into().into_raw(),
1005                // This seems useless to expose.
1006                pvUserData: null_mut(),
1007                dwCurrentState: current_state.bits(),
1008                dwEventState: State::UNAWARE.bits(),
1009                cbAtr: 0,
1010                rgbAtr: [0; ffi::ATR_BUFFER_SIZE],
1011            },
1012        }
1013    }
1014
1015    /// The name of the card reader.
1016    pub fn name(&self) -> &CStr {
1017        // Lifetime elision assigns this the same lifetime as &self; this
1018        // is what we want, and is safe.
1019        unsafe { CStr::from_ptr(self.inner.szReader) }
1020    }
1021
1022    /// The ATR (Answer To Reset) of the card inserted to the reader.
1023    pub fn atr(&self) -> &[u8] {
1024        &self.inner.rgbAtr[0..self.inner.cbAtr as usize]
1025    }
1026
1027    /// The last current state that was set.
1028    pub fn current_state(&self) -> State {
1029        State::from_bits_truncate(self.inner.dwCurrentState)
1030    }
1031
1032    /// The last reported state.
1033    pub fn event_state(&self) -> State {
1034        State::from_bits_truncate(self.inner.dwEventState)
1035    }
1036
1037    /// The card event count.
1038    ///
1039    /// The count is incremented for each card insertion or removal in the
1040    /// reader. This can be used to detect a card removal/insertion
1041    /// between two calls to `Context::get_status_change()`.
1042    pub fn event_count(&self) -> u32 {
1043        ((self.inner.dwEventState & 0xFFFF_0000) >> 16) as u32
1044    }
1045
1046    /// Sync the currently-known state to the last reported state.
1047    pub fn sync_current_state(&mut self) {
1048        // In windows it is important that the event count is included;
1049        // otherwise PNP_NOTIFICATION is always reported as changed:
1050        // https://stackoverflow.com/a/16467368
1051        self.inner.dwCurrentState = self.inner.dwEventState;
1052    }
1053}
1054
1055impl Drop for ReaderState {
1056    fn drop(&mut self) {
1057        // Reclaim the name and drop it immediately.
1058        unsafe { drop(CString::from_raw(self.inner.szReader as *mut c_char)) };
1059    }
1060}
1061
1062unsafe impl Send for ReaderState {}
1063unsafe impl Sync for ReaderState {}
1064
1065/// Status of a card in a card reader.
1066#[derive(Clone, Debug)]
1067pub struct CardStatus<'names_buf, 'atr_buf> {
1068    reader_names: ReaderNames<'names_buf>,
1069    state: DWORD,
1070    protocol: Option<Protocol>,
1071    atr: &'atr_buf [u8],
1072}
1073
1074impl<'names_buf, 'atr_buf> CardStatus<'names_buf, 'atr_buf> {
1075    /// Iterator over the names by which the connected card reader is known.
1076    pub fn reader_names(&self) -> ReaderNames<'names_buf> {
1077        self.reader_names.clone()
1078    }
1079
1080    /// Current status of the smart card in the reader.
1081    pub fn status(&self) -> Status {
1082        Status::from_raw(self.state)
1083    }
1084
1085    /// Current protocol of the card, if any.
1086    ///
1087    /// The value is meaningful only if a communication protocol has already
1088    /// been established.
1089    ///
1090    /// If connected to a reader directly without an active protocol, returns
1091    /// None.
1092    pub fn protocol2(&self) -> Option<Protocol> {
1093        self.protocol
1094    }
1095
1096    /// Current protocol of the card, if any.
1097    ///
1098    /// The value is meaningful only if a communication protocol has already
1099    /// been established.
1100    ///
1101    /// ## Panics
1102    ///
1103    /// This function panics when connected to a reader directly without an
1104    /// active protocol. Use `protocol2()` instead if you want to avoid this.
1105    pub fn protocol(&self) -> Protocol {
1106        self.protocol
1107            .expect("pcsc::CardStatus::protocol() does not support direct connections; use protocol2() instead")
1108    }
1109
1110    /// The current ATR string of the card.
1111    pub fn atr(&self) -> &'atr_buf [u8] {
1112        self.atr
1113    }
1114}
1115
1116/// Status of a card in a card reader (owned).
1117///
1118/// This is an owned version of [`CardStatus`](struct.CardStatus.html).
1119#[derive(Clone, Debug)]
1120pub struct CardStatusOwned {
1121    reader_names: Vec<CString>,
1122    state: DWORD,
1123    protocol: Option<Protocol>,
1124    atr: Vec<u8>,
1125}
1126
1127impl CardStatusOwned {
1128    /// Slice of the names by which the connected card reader is known.
1129    pub fn reader_names(&self) -> &[CString] {
1130        &self.reader_names
1131    }
1132
1133    /// Current status of the smart card in the reader.
1134    pub fn status(&self) -> Status {
1135        Status::from_raw(self.state)
1136    }
1137
1138    /// Current protocol of the card, if any.
1139    ///
1140    /// The value is meaningful only if a communication protocol has already
1141    /// been established.
1142    ///
1143    /// If connected to a reader directly without an active protocol, returns
1144    /// None.
1145    pub fn protocol2(&self) -> Option<Protocol> {
1146        self.protocol
1147    }
1148
1149    /// Current protocol of the card, if any.
1150    ///
1151    /// The value is meaningful only if a communication protocol has already
1152    /// been established.
1153    ///
1154    /// ## Panics
1155    ///
1156    /// This function panics when connected to a reader directly without an
1157    /// active protocol. Use `protocol2()` instead if you want to avoid this.
1158    pub fn protocol(&self) -> Protocol {
1159        self.protocol
1160            .expect("pcsc::CardStatus::protocol() does not support direct connections; use protocol2() instead")
1161    }
1162
1163    /// The current ATR string of the card.
1164    pub fn atr(&self) -> &[u8] {
1165        &self.atr
1166    }
1167}
1168
1169impl Card {
1170    /// Start a new exclusive transaction with the card.
1171    ///
1172    /// Operations on the card for the duration of the transaction
1173    /// can only be performed through the returned `Transaction`.
1174    ///
1175    /// This function wraps `SCardBeginTransaction` ([pcsclite][1],
1176    /// [MSDN][2]).
1177    ///
1178    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#gaddb835dce01a0da1d6ca02d33ee7d861
1179    /// [2]: https://msdn.microsoft.com/en-us/library/aa379469.aspx
1180    pub fn transaction(&mut self) -> Result<Transaction, Error> {
1181        unsafe {
1182            try_pcsc!(ffi::SCardBeginTransaction(self.handle,));
1183
1184            Ok(Transaction { card: self })
1185        }
1186    }
1187
1188    /// Start a new exclusive transaction with the card.
1189    ///
1190    /// Operations on the card for the duration of the transaction
1191    /// can only be performed through the returned `Transaction`.
1192    ///
1193    /// This function is like [`Card::transaction`], but also returns the
1194    /// reference to `self` on error. When starting a transaction, you might
1195    /// want to deal with transient errors, like [`Error::ResetCard`], by
1196    /// reconnecting to the card, and retrying the transaction. When this
1197    /// functionality is wrapped, this doesn't work, because mutable references
1198    /// can't be reborrowed (at least in current Rust). This function returns
1199    /// the reference, which allows this construct.
1200    ///
1201    /// This function wraps `SCardBeginTransaction` ([pcsclite][1],
1202    /// [MSDN][2]).
1203    ///
1204    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#gaddb835dce01a0da1d6ca02d33ee7d861
1205    /// [2]: https://msdn.microsoft.com/en-us/library/aa379469.aspx
1206    pub fn transaction2(&mut self) -> Result<Transaction, (&mut Self, Error)> {
1207        unsafe {
1208            let err = ffi::SCardBeginTransaction(self.handle);
1209            if err != ffi::SCARD_S_SUCCESS {
1210                return Err((self, Error::from_raw(err)));
1211            }
1212
1213            Ok(Transaction { card: self })
1214        }
1215    }
1216
1217    /// Reconnect to the card.
1218    ///
1219    /// This function wraps `SCardReconnect` ([pcsclite][1], [MSDN][2]).
1220    ///
1221    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#gad5d4393ca8c470112ad9468c44ed8940
1222    /// [2]: https://msdn.microsoft.com/en-us/library/aa379797.aspx
1223    pub fn reconnect(
1224        &mut self,
1225        share_mode: ShareMode,
1226        preferred_protocols: Protocols,
1227        initialization: Disposition,
1228    ) -> Result<(), Error> {
1229        unsafe {
1230            let mut raw_active_protocol: DWORD = DUMMY_DWORD;
1231
1232            try_pcsc!(ffi::SCardReconnect(
1233                self.handle,
1234                share_mode.into_raw(),
1235                preferred_protocols.bits(),
1236                initialization.into_raw(),
1237                &mut raw_active_protocol,
1238            ));
1239
1240            self.active_protocol = Protocol::from_raw(raw_active_protocol);
1241
1242            Ok(())
1243        }
1244    }
1245
1246    /// Disconnect from the card.
1247    ///
1248    /// In case of error, ownership of the card is returned to the caller.
1249    ///
1250    /// This function wraps `SCardDisconnect` ([pcsclite][1], [MSDN][2]).
1251    ///
1252    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#ga4be198045c73ec0deb79e66c0ca1738a
1253    /// [2]: https://msdn.microsoft.com/en-us/library/aa379475.aspx
1254    ///
1255    /// ## Note
1256    ///
1257    /// `Card` implements `Drop` which automatically disconnects the card
1258    /// using `Disposition::ResetCard`; you only need to call this
1259    /// function if you want to handle errors or use a different
1260    /// disposition method.
1261    pub fn disconnect(mut self, disposition: Disposition) -> Result<(), (Card, Error)> {
1262        unsafe {
1263            let err = ffi::SCardDisconnect(self.handle, disposition.into_raw());
1264            if err != ffi::SCARD_S_SUCCESS {
1265                return Err((self, Error::from_raw(err)));
1266            }
1267
1268            // Skip the drop, we did it "manually".
1269            std::ptr::drop_in_place(&mut self._context);
1270            forget(self);
1271
1272            Ok(())
1273        }
1274    }
1275
1276    /// Get current info on the card.
1277    ///
1278    /// This function wraps `SCardStatus` ([pcsclite][1], [MSDN][2]).
1279    ///
1280    /// ## Deprecated
1281    ///
1282    /// The reader names and ATR return values are missing.
1283    ///
1284    /// When there is no active protocol (as when connecting to the reader
1285    /// directly), this function panics.
1286    ///
1287    /// Use `status2()` or `status2_owned()` instead.
1288    ///
1289    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#gae49c3c894ad7ac12a5b896bde70d0382
1290    /// [2]: https://msdn.microsoft.com/en-us/library/aa379803.aspx
1291    #[deprecated(since = "2.3.0", note = "Use status2() or status2_owned() instead.")]
1292    pub fn status(&self) -> Result<(Status, Protocol), Error> {
1293        unsafe {
1294            let mut raw_status: DWORD = DUMMY_DWORD;
1295            let mut raw_protocol: DWORD = DUMMY_DWORD;
1296
1297            try_pcsc!(ffi::SCardStatus(
1298                self.handle,
1299                null_mut(),
1300                null_mut(),
1301                &mut raw_status,
1302                &mut raw_protocol,
1303                null_mut(),
1304                null_mut(),
1305            ));
1306
1307            let status = Status::from_raw(raw_status);
1308
1309            let protocol = Protocol::from_raw(raw_protocol)
1310                .expect("pcsc::Card::status() does not support direct connections; use status2() instead");
1311
1312            Ok((status, protocol))
1313        }
1314    }
1315
1316    /// Get current info on the card.
1317    ///
1318    /// `names_buffer` is a buffer that should be large enough to hold all of
1319    /// the reader names.
1320    ///
1321    /// `atr_buffer` is a buffer that should be large enough to hold the ATR.
1322    /// The recommended size is `MAX_ATR_SIZE`, which should be always
1323    /// sufficent.
1324    ///
1325    /// The function `status2_len` can be used to find the exact required
1326    /// lengths.
1327    ///
1328    /// If the buffers are not large enough to hold all of the names or the
1329    /// ATR, `Error::InsufficientBuffer` is returned.
1330    ///
1331    /// This function wraps `SCardStatus` ([pcsclite][1], [MSDN][2]).
1332    ///
1333    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#gae49c3c894ad7ac12a5b896bde70d0382
1334    /// [2]: https://msdn.microsoft.com/en-us/library/aa379803.aspx
1335    pub fn status2<'names_buf, 'atr_buf>(
1336        &self,
1337        names_buffer: &'names_buf mut [u8],
1338        atr_buffer: &'atr_buf mut [u8],
1339    ) -> Result<CardStatus<'names_buf, 'atr_buf>, Error> {
1340        unsafe {
1341            assert!(names_buffer.len() <= std::u32::MAX as usize);
1342            let mut names_len: DWORD = names_buffer.len() as DWORD;
1343            let mut raw_state: DWORD = DUMMY_DWORD;
1344            let mut raw_protocol: DWORD = DUMMY_DWORD;
1345            assert!(atr_buffer.len() <= std::u32::MAX as usize);
1346            let mut atr_len: DWORD = atr_buffer.len() as DWORD;
1347
1348            try_pcsc!(ffi::SCardStatus(
1349                self.handle,
1350                names_buffer.as_mut_ptr() as *mut c_char,
1351                &mut names_len,
1352                &mut raw_state,
1353                &mut raw_protocol,
1354                atr_buffer.as_mut_ptr(),
1355                &mut atr_len,
1356            ));
1357
1358            Ok(CardStatus {
1359                reader_names: ReaderNames {
1360                    buf: &names_buffer[..names_len as usize],
1361                    pos: 0,
1362                },
1363                state: raw_state,
1364                protocol: Protocol::from_raw(raw_protocol),
1365                atr: &atr_buffer[0..atr_len as usize],
1366            })
1367        }
1368    }
1369
1370    /// Get the needed length of the names buffer (first result) and ATR buffer
1371    /// (second result) to be passed to `status2`.
1372    ///
1373    /// This function wraps `SCardStatus` ([pcsclite][1], [MSDN][2]).
1374    ///
1375    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#gae49c3c894ad7ac12a5b896bde70d0382
1376    /// [2]: https://msdn.microsoft.com/en-us/library/aa379803.aspx
1377    pub fn status2_len(&self) -> Result<(usize, usize), Error> {
1378        unsafe {
1379            let mut names_len: DWORD = DUMMY_DWORD;
1380            let mut raw_state: DWORD = DUMMY_DWORD;
1381            let mut raw_protocol: DWORD = DUMMY_DWORD;
1382            let mut atr_len: DWORD = DUMMY_DWORD;
1383
1384            try_pcsc!(ffi::SCardStatus(
1385                self.handle,
1386                null_mut(),
1387                &mut names_len,
1388                &mut raw_state,
1389                &mut raw_protocol,
1390                null_mut(),
1391                &mut atr_len,
1392            ));
1393
1394            Ok((names_len as usize, atr_len as usize))
1395        }
1396    }
1397
1398    /// Get current info on the card, allocating buffers of the required size.
1399    ///
1400    /// This function wraps `SCardStatus` ([pcsclite][1], [MSDN][2]).  It is an owned version of
1401    /// [`status2`](#method.status2), calling [`status2_len`](#method.status2_len) to determine the
1402    /// required buffer sizes.
1403    ///
1404    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#gae49c3c894ad7ac12a5b896bde70d0382
1405    /// [2]: https://msdn.microsoft.com/en-us/library/aa379803.aspx
1406    pub fn status2_owned(&self) -> Result<CardStatusOwned, Error> {
1407        let (names_len, atr_len) = self.status2_len()?;
1408        let mut names_buffer = vec![0u8; names_len];
1409        let mut atr_buffer = vec![0u8; atr_len];
1410
1411        let (reader_names, state, protocol, atr_len) = {
1412            let card_status = self.status2(&mut names_buffer, &mut atr_buffer)?;
1413            let reader_names = card_status.reader_names.map(ToOwned::to_owned).collect();
1414            (
1415                reader_names,
1416                card_status.state,
1417                card_status.protocol,
1418                card_status.atr.len(),
1419            )
1420        };
1421
1422        atr_buffer.truncate(atr_len);
1423
1424        Ok(CardStatusOwned {
1425            reader_names,
1426            state,
1427            protocol,
1428            atr: atr_buffer,
1429        })
1430    }
1431
1432    /// Get an attribute of the card or card reader.
1433    ///
1434    /// `buffer` is a buffer that should be large enough for the attribute
1435    /// data. The function `get_attribute_len` can be used to find the
1436    /// exact required length.
1437    ///
1438    /// Returns a slice into `buffer` containing the attribute data.
1439    ///
1440    /// If the buffer is not large enough, `Error::InsufficientBuffer` is
1441    /// returned.
1442    ///
1443    /// This function wraps `SCardGetAttrib` ([pcsclite][1], [MSDN][2]).
1444    ///
1445    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#gaacfec51917255b7a25b94c5104961602
1446    /// [2]: https://msdn.microsoft.com/en-us/library/aa379559.aspx
1447    pub fn get_attribute<'buf>(&self, attribute: Attribute, buffer: &'buf mut [u8]) -> Result<&'buf [u8], Error> {
1448        unsafe {
1449            assert!(buffer.len() <= std::u32::MAX as usize);
1450            let mut attribute_len = buffer.len() as DWORD;
1451            // SCardGetAttribtreats null specially, to query the needed
1452            // buffer length. We don't want the caller to be able to trigger
1453            // this here -- should use `get_attribute_len` instead. So needs
1454            // some special treatment.
1455            let bufptr = if attribute_len == 0 {
1456                null_mut()
1457            } else {
1458                let ptr = buffer.as_mut_ptr();
1459                assert!(!ptr.is_null());
1460                ptr
1461            };
1462
1463            try_pcsc!(ffi::SCardGetAttrib(
1464                self.handle,
1465                attribute.into_raw(),
1466                buffer.as_mut_ptr(),
1467                &mut attribute_len,
1468            ));
1469            if bufptr.is_null() && attribute_len > 0 {
1470                return Err(Error::InsufficientBuffer);
1471            }
1472
1473            Ok(&buffer[0..attribute_len as usize])
1474        }
1475    }
1476
1477    /// Get the needed length of a buffer to be passed to `get_attribute`.
1478    ///
1479    /// This function wraps `SCardGetAttrib` ([pcsclite][1], [MSDN][2]).
1480    ///
1481    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#gaacfec51917255b7a25b94c5104961602
1482    /// [2]: https://msdn.microsoft.com/en-us/library/aa379559.aspx
1483    pub fn get_attribute_len(&self, attribute: Attribute) -> Result<usize, Error> {
1484        unsafe {
1485            let mut attribute_len = DUMMY_DWORD;
1486
1487            try_pcsc!(ffi::SCardGetAttrib(
1488                self.handle,
1489                attribute.into_raw(),
1490                null_mut(),
1491                &mut attribute_len,
1492            ));
1493
1494            Ok(attribute_len as usize)
1495        }
1496    }
1497
1498    /// Get an attribute of the card or card reader, allocating a buffer of the required size.
1499    ///
1500    /// This function wraps `SCardGetAttrib` ([pcsclite][1], [MSDN][2]).  It is an owned version of
1501    /// [`get_attribute`](#method.get_attribute), calling
1502    /// [`get_attribute_len`](#method.get_attribute_len) to determine the required buffer size.
1503    ///
1504    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#gaacfec51917255b7a25b94c5104961602
1505    /// [2]: https://msdn.microsoft.com/en-us/library/aa379559.aspx
1506    pub fn get_attribute_owned(&self, attribute: Attribute) -> Result<Vec<u8>, Error> {
1507        let len = self.get_attribute_len(attribute)?;
1508        if len == 0 {
1509            return Ok(vec![]);
1510        }
1511        let mut buf = vec![0u8; len];
1512        let n = self.get_attribute(attribute, &mut buf)?.len();
1513        buf.truncate(n);
1514        Ok(buf)
1515    }
1516
1517    /// Set an attribute of the card or card reader.
1518    ///
1519    /// This function wraps `SCardSetAttrib` ([pcsclite][1], [MSDN][2]).
1520    ///
1521    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#ga060f0038a4ddfd5dd2b8fadf3c3a2e4f
1522    /// [2]: https://msdn.microsoft.com/en-us/library/aa379801.aspx
1523    pub fn set_attribute(&self, attribute: Attribute, attribute_data: &[u8]) -> Result<(), Error> {
1524        unsafe {
1525            assert!(attribute_data.len() <= std::u32::MAX as usize);
1526
1527            try_pcsc!(ffi::SCardSetAttrib(
1528                self.handle,
1529                attribute.into_raw(),
1530                attribute_data.as_ptr(),
1531                attribute_data.len() as DWORD,
1532            ));
1533
1534            Ok(())
1535        }
1536    }
1537
1538    /// Transmit an APDU command to the card.
1539    ///
1540    /// `receive_buffer` is a buffer that should be large enough to hold
1541    /// the APDU response.
1542    ///
1543    /// Returns a slice into `receive_buffer` containing the APDU
1544    /// response.
1545    ///
1546    /// If `receive_buffer` is not large enough to hold the APDU response,
1547    /// `Error::InsufficientBuffer` is returned.
1548    ///
1549    /// This function wraps `SCardTransmit` ([pcsclite][1], [MSDN][2]).
1550    ///
1551    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#ga9a2d77242a271310269065e64633ab99
1552    /// [2]: https://msdn.microsoft.com/en-us/library/aa379804.aspx
1553    pub fn transmit<'buf>(&self, send_buffer: &[u8], receive_buffer: &'buf mut [u8]) -> Result<&'buf [u8], Error> {
1554        self.transmit2(send_buffer, receive_buffer).map_err(|(err, _)| err)
1555    }
1556
1557    /// Transmit an APDU command to the card.
1558    ///
1559    /// This functions works like [transmit](#method.transmit) but the error type is
1560    /// `(Error, usize)`.
1561    ///
1562    /// `receive_buffer` is a buffer that should be large enough to hold
1563    /// the APDU response.
1564    ///
1565    /// Returns a slice into `receive_buffer` containing the APDU
1566    /// response.
1567    ///
1568    /// If `receive_buffer` is not large enough to hold the APDU response,
1569    /// `Error::InsufficientBuffer` is returned, and the `usize` value is set to the
1570    /// required size.
1571    ///
1572    /// `usize` value of the error has no meaning for other `Error` values than `Error::InsufficientBuffer`.
1573    ///
1574    /// **Note** that when `Error::InsufficientBuffer` is returned, the provided command has
1575    /// been already effectively executed by the card. Do not treat this as a generic way to
1576    /// obtain the size of the response as you may end up issuing commands multiple times
1577    /// which can lead to unexpected results. Normally, most operations on standard card
1578    /// let you know the expected size of the response in advance. Be sure to only use this
1579    /// for commands that may be executed multiple times in a row without changing the state
1580    /// of the card.
1581    ///
1582    /// This function wraps `SCardTransmit` ([pcsclite][1], [MSDN][2]).
1583    ///
1584    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#ga9a2d77242a271310269065e64633ab99
1585    /// [2]: https://msdn.microsoft.com/en-us/library/aa379804.aspx
1586    pub fn transmit2<'buf>(
1587        &self,
1588        send_buffer: &[u8],
1589        receive_buffer: &'buf mut [u8],
1590    ) -> Result<&'buf [u8], (Error, usize)> {
1591        let active_protocol = self
1592            .active_protocol
1593            .expect("pcsc::Card::transmit() does not work with direct connections");
1594        let send_pci = get_protocol_pci(active_protocol);
1595        let recv_pci = null_mut();
1596        assert!(receive_buffer.len() <= std::u32::MAX as usize);
1597        let mut receive_len = receive_buffer.len() as DWORD;
1598
1599        unsafe {
1600            assert!(send_buffer.len() <= std::u32::MAX as usize);
1601
1602            let r = ffi::SCardTransmit(
1603                self.handle,
1604                send_pci,
1605                send_buffer.as_ptr(),
1606                send_buffer.len() as DWORD,
1607                recv_pci,
1608                receive_buffer.as_mut_ptr(),
1609                &mut receive_len,
1610            );
1611
1612            match r {
1613                ffi::SCARD_S_SUCCESS => (),
1614                err => return Err((Error::from_raw(err), receive_len as usize)),
1615            }
1616
1617            Ok(&receive_buffer[0..receive_len as usize])
1618        }
1619    }
1620
1621    /// Sends a command directly to the reader (driver).
1622    ///
1623    /// `control_code` is the reader-specific control code. You may need
1624    /// to pass it through the `ctl_code()` function, according to the
1625    /// driver documentation.
1626    ///
1627    /// `receive_buffer` is a buffer that should be large enough to hold
1628    /// the response.
1629    ///
1630    /// Returns a slice into `receive_buffer` containing the response.
1631    ///
1632    /// If `receive_buffer` is not large enough to hold the response,
1633    /// `Error::InsufficientBuffer` is returned.
1634    ///
1635    /// This function wraps `SCardControl` ([pcsclite][1], [MSDN][2]).
1636    ///
1637    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#gac3454d4657110fd7f753b2d3d8f4e32f
1638    /// [2]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa379474(v=vs.85).aspx
1639    pub fn control<'buf>(
1640        &self,
1641        // TODO: This is a portability hazard -- should change to u32
1642        //       in the next breaking change release.
1643        control_code: DWORD,
1644        send_buffer: &[u8],
1645        receive_buffer: &'buf mut [u8],
1646    ) -> Result<&'buf [u8], Error> {
1647        let mut receive_len: DWORD = DUMMY_DWORD;
1648
1649        unsafe {
1650            assert!(send_buffer.len() <= std::u32::MAX as usize);
1651            assert!(receive_buffer.len() <= std::u32::MAX as usize);
1652
1653            try_pcsc!(ffi::SCardControl(
1654                self.handle,
1655                control_code,
1656                send_buffer.as_ptr(),
1657                send_buffer.len() as DWORD,
1658                receive_buffer.as_mut_ptr(),
1659                receive_buffer.len() as DWORD,
1660                &mut receive_len,
1661            ));
1662
1663            Ok(&receive_buffer[0..receive_len as usize])
1664        }
1665    }
1666}
1667
1668impl Drop for Card {
1669    fn drop(&mut self) {
1670        unsafe {
1671            // Error is ignored here; to do proper error handling,
1672            // disconnect() should be called manually.
1673            //
1674            // Disposition is hard-coded to ResetCard here; to use
1675            // another method, disconnect() should be called manually.
1676            let _err = ffi::SCardDisconnect(self.handle, Disposition::ResetCard.into_raw());
1677        }
1678    }
1679}
1680
1681unsafe impl Send for Card {}
1682unsafe impl Sync for Card {}
1683
1684impl<'tx> Transaction<'tx> {
1685    /// End the transaction.
1686    ///
1687    /// In case of error, ownership of the transaction is returned to the
1688    /// caller.
1689    ///
1690    /// This function wraps `SCardEndTransaction` ([pcsclite][1],
1691    /// [MSDN][2]).
1692    ///
1693    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#gae8742473b404363e5c587f570d7e2f3b
1694    /// [2]: https://msdn.microsoft.com/en-us/library/aa379477.aspx
1695    ///
1696    /// ## Note
1697    ///
1698    /// `Transaction` implements `Drop` which automatically ends the
1699    /// transaction using `Disposition::LeaveCard`; you only need to call
1700    /// this function if you want to handle errors or use a different
1701    /// disposition method.
1702    pub fn end(self, disposition: Disposition) -> Result<(), (Transaction<'tx>, Error)> {
1703        unsafe {
1704            let err = ffi::SCardEndTransaction(self.card.handle, disposition.into_raw());
1705            if err != 0 {
1706                return Err((self, Error::from_raw(err)));
1707            }
1708
1709            // Skip the drop, we did it "manually".
1710            forget(self);
1711
1712            Ok(())
1713        }
1714    }
1715
1716    /// Reconnect to the card.
1717    ///
1718    /// This function wraps `SCardReconnect` ([pcsclite][1], [MSDN][2]).
1719    ///
1720    /// [1]: https://pcsclite.apdu.fr/api/group__API.html#gad5d4393ca8c470112ad9468c44ed8940
1721    /// [2]: https://msdn.microsoft.com/en-us/library/aa379797.aspx
1722    pub fn reconnect(
1723        &mut self,
1724        share_mode: ShareMode,
1725        preferred_protocols: Protocols,
1726        initialization: Disposition,
1727    ) -> Result<(), Error> {
1728        self.card.reconnect(share_mode, preferred_protocols, initialization)
1729    }
1730}
1731
1732impl<'tx> Drop for Transaction<'tx> {
1733    fn drop(&mut self) {
1734        unsafe {
1735            // Error is ignored here; to do proper error handling,
1736            // end() should be called manually.
1737            //
1738            // Disposition is hard-coded to LeaveCard here; to use
1739            // another method, end() should be called manually.
1740            let _err = ffi::SCardEndTransaction(self.card.handle, Disposition::LeaveCard.into_raw());
1741        }
1742    }
1743}
1744
1745impl<'tx> Deref for Transaction<'tx> {
1746    type Target = Card;
1747
1748    fn deref(&self) -> &Card {
1749        self.card
1750    }
1751}