/*
*/
/**
*
* @author
*/
package uk.co.nimp.scard;
import com.atolsystems.atolutilities.AStringUtilities;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.smartcardio.CardException;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.ptr.ByteByReference;
import com.sun.jna.ptr.ShortByReference;
import com.sun.jna.ptr.*;
import com.sun.jna.ptr.PointerByReference;
import com.sun.jna.ptr.NativeLongByReference;
import com.sun.jna.StringArray;
import com.sun.jna.WString;
import java.io.File;
import java.io.IOException;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class PcScTerminalManager extends GenericTerminalManager {
static public interface Win32PCSC extends Library {
/*LONG SCardEstablishContext(
__in DWORD dwScope,
__in LPCVOID pvReserved1,
__in LPCVOID pvReserved2,
__out LPSCARDCONTEXT phContext
);*/
NativeLong SCardEstablishContext(
int dwScope,
Pointer pvReserved1,
Pointer pvReserved2,
NativeLongByReference phContext);
/*LONG SCardReleaseContext(
__in SCARDCONTEXT hContext
);*/
NativeLong SCardReleaseContext(
NativeLong hContext);
/*LONG SCardFreeMemory(
__in SCARDCONTEXT hContext,
__in LPCVOID pvMem
);*/
/*NativeLong SCardFreeMemory(
NativeLong hContext,
Pointer pvMem);*/
/*LONG SCardListReaders(
__in SCARDCONTEXT hContext,
__in LPCTSTR mszGroups,
__out LPTSTR mszReaders,
__inout LPDWORD pcchReaders
);*/
NativeLong SCardListReadersW(
NativeLong hContext,
String mszGroups,
//PointerByReference mszReaders,
short[] mszReaders,
ShortByReference pcchReaders);
//NativeLong pcchReaders);
//Short pcchReaders);
/*typedef struct {
LPCTSTR szReader;
LPVOID pvUserData;
DWORD dwCurrentState;
DWORD dwEventState;
DWORD cbAtr;
BYTE rgbAtr[36];
}SCARD_READERSTATE, *PSCARD_READERSTATE, *LPSCARD_READERSTATE;*/
public static class SCARD_READERSTATE extends Structure {
public WString szReader;
public Pointer pvUserData;
public int dwCurrentState;
public int dwEventState;
public int cbAtr;
public byte rgbAtr[];//36];
}
/*LONG SCardGetStatusChange(
__in SCARDCONTEXT hContext,
__in DWORD dwTimeout,
__inout LPSCARD_READERSTATE rgReaderStates,
__in DWORD cReaders
);*/
NativeLong SCardGetStatusChangeW(
NativeLong hContext,
int dwTimeout,
SCARD_READERSTATE rgReaderStates[],
int cReaders);
/*LONG SCardConnect(
__in SCARDCONTEXT hContext,
__in LPCTSTR szReader,
__in DWORD dwShareMode,
__in DWORD dwPreferredProtocols,
__out LPSCARDHANDLE phCard,
__out LPDWORD pdwActiveProtocol
);*/
NativeLong SCardConnectW(
NativeLong hContext,
WString szReader,
int dwShareMode,
int dwPreferredProtocols,
NativeLongByReference phCard,
IntByReference pdwActiveProtocol);
/*LONG WINAPI SCardReconnect(
__in SCARDHANDLE hCard,
__in DWORD dwShareMode,
__in DWORD dwPreferredProtocols,
__in DWORD dwInitialization,
__out_opt LPDWORD pdwActiveProtocol
);*/
NativeLong SCardReconnect(
NativeLong hCard,
int dwShareMode,
int dwPreferredProtocols,
int dwInitialization,
IntByReference pdwActiveProtocol
);
/*LONG SCardDisconnect(
__in SCARDHANDLE hCard,
__in DWORD dwDisposition
);*/
NativeLong SCardDisconnect(
NativeLong hCard,
int dwDisposition);
/*LONG SCardStatus(
__in SCARDHANDLE hCard,
__out LPTSTR szReaderName,
__inout LPDWORD pcchReaderLen,
__out LPDWORD pdwState,
__out LPDWORD pdwProtocol,
__out LPBYTE pbAtr,
__inout LPDWORD pcbAtrLen
);*/
NativeLong SCardStatusW(
NativeLong hCard,
WString szReaderName,
IntByReference pcchReaderLen,
IntByReference pdwState,
IntByReference pdwProtocol,
byte pbAtr[],
IntByReference pcbAtrLen);
/*typedef struct {
DWORD dwProtocol;
DWORD cbPciLength;
}SCARD_IO_REQUEST;*/
public static class SCARD_IO_REQUEST extends Structure {
public int dwProtocol;
public int cbPciLength;
}
/* LONG SCardTransmit(
__in SCARDHANDLE hCard,
__in LPCSCARD_IO_REQUEST pioSendPci,
__in LPCBYTE pbSendBuffer,
__in DWORD cbSendLength,
__inout LPSCARD_IO_REQUEST pioRecvPci,
__out LPBYTE pbRecvBuffer,
__inout LPDWORD pcbRecvLength
);*/
NativeLong SCardTransmit(
NativeLong hCard,
SCARD_IO_REQUEST pioSendPci,
byte[] pbSendBuffer,
int cbSendLength,
SCARD_IO_REQUEST pioRecvPci,
byte[] pbRecvBuffer,
IntByReference pcbRecvLength);
/* LONG WINAPI SCardGetAttrib(
__in SCARDHANDLE hCard,
__in DWORD dwAttrId,
__out LPBYTE pbAttr,
__inout LPDWORD pcbAttrLen
);*/
NativeLong SCardGetAttrib(
NativeLong hCard,
int dwAttrId,
byte[] pbAttr,
IntByReference pcbAttrLen);
}
static final Throwable initException;
static final Win32PCSC win32PCSC;
static final String CONF_FILE_NAME="PcScTerminalManager.conf";
static final String LIB_NAME="winscard";
private static long contextId=0;
static {
Throwable ex;
Win32PCSC trial;
try {
trial = (Win32PCSC) Native.loadLibrary(LIB_NAME, Win32PCSC.class);
ex = null;
} catch (Throwable e) {
ex = e;
trial = null;
}
win32PCSC = trial;
initException = ex;
if(null==initException){
try {
PcScTerminalManager.initContext();
//GenericTerminalManager.registerTerminalManager(PcScTerminalManager.class);
} catch (ScardException ex1) {
Logger.getLogger(PcScTerminalManager.class.getName()).log(Level.SEVERE, null, ex1);
}
}
}
public PcScTerminalManager(){
}
protected String getConfFileName(){
return CONF_FILE_NAME;
}
@Override
protected void loadConfigurationImpl(File argFile) throws Exception {
if(null!=initException)
throw new Exception(initException);
if(null==win32PCSC)
throw new NullPointerException(LIB_NAME+" could not be loaded");
}
static public /*synchronized*/ void initContext() throws ScardException {
if (contextId == 0) {
NativeLongByReference phContext = new NativeLongByReference();
phContext.setValue(new NativeLong((long) 0));
NativeLong status = win32PCSC.SCardEstablishContext(SCARD_SCOPE_USER, Pointer.NULL, Pointer.NULL, phContext);
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
if(0!=contextId)
win32PCSC.SCardReleaseContext(new NativeLong(contextId));
}
});
if (0 != status.longValue()) {
throw new PcScException(status.intValue());
}
contextId = phContext.getValue().longValue();
}
}
/**
*
* @param win32Status
* @return the equivalent GenericTerminal.State
*/
static protected GenericTerminal.State win32Status2GenericTerminalState(byte win32Status){
if(SCARD_ABSENT==win32Status)
return GenericTerminal.State.CARD_ABSENT;
return GenericTerminal.State.CARD_PRESENT;
}
/**
*
* @param win32Protocol
* @return the equivalent GenericTerminal protocol code
*/
static protected int win32Protocol2GenericTerminalProtocol(byte win32Protocol){
switch(win32Protocol)
{
case SCARD_PROTOCOL_T0:
return GenericTerminal.PROTOCOL_T_0;
case SCARD_PROTOCOL_T1:
return GenericTerminal.PROTOCOL_T_1;
default:
return GenericTerminal.PROTOCOL_UNDEFINED;
}
}
@Override
public List<GenericTerminal> list() throws ScardException {
ShortByReference requiredSize = new ShortByReference();
NativeLong status = win32PCSC.SCardListReadersW(new NativeLong(contextId), null, null, requiredSize);
List<GenericTerminal> terminalsList;
if (SCARD_E_NO_READERS_AVAILABLE == status.longValue()) {
terminalsList = new ArrayList<GenericTerminal>(0);
return Collections.unmodifiableList(terminalsList);
}
if (SCARD_S_SUCCESS != status.longValue()) {
throw new PcScException(status.intValue());
}
short nativeOut[] = new short[requiredSize.getValue()];
status = win32PCSC.SCardListReadersW(new NativeLong(contextId), null, nativeOut, requiredSize);
if (SCARD_S_SUCCESS != status.longValue()) {
throw new PcScException(status.intValue());
}
String[] readerNames=AStringUtilities.shortsToStrings(nativeOut);
terminalsList = new ArrayList<GenericTerminal>(readerNames.length);
for (int i=0;i<readerNames.length;i++) {
String name=readerNames[i];
GenericTerminal terminal = getTerminalImpl(name);
terminalsList.add(terminal);
//byte[] terminalStatus={0,0};
//SCardStatus(contextId, terminalStatus);
int[] terminalStatus = SCardGetStatusChange(contextId, 0,
new int[] {SCARD_STATE_UNAWARE}, new String[] {name});
GenericTerminal.State newState=win32Status2GenericTerminalState((byte)terminalStatus[0]);
switch(newState){
case CARD_ABSENT:
if((GenericTerminal.State.CARD_PRESENT==terminal.state)||
(GenericTerminal.State.CARD_INSERTION==terminal.state))
terminal.state=GenericTerminal.State.CARD_REMOVAL;
else
terminal.state=GenericTerminal.State.CARD_ABSENT;
break;
case CARD_PRESENT:
if((GenericTerminal.State.CARD_ABSENT==terminal.state)||
(GenericTerminal.State.CARD_REMOVAL==terminal.state))
terminal.state=GenericTerminal.State.CARD_INSERTION;
else
terminal.state=GenericTerminal.State.CARD_PRESENT;
break;
}
}
return Collections.unmodifiableList(terminalsList);
}
private static final Map<String,Reference<GenericTerminal>> terminals
= new HashMap<String,Reference<GenericTerminal>>();
protected static /*synchronized*/ GenericTerminal getTerminalImpl(String name) {
synchronized(terminals){
Reference<GenericTerminal> ref = terminals.get(name);
GenericTerminal terminal = (ref != null) ? ref.get() : null;
if (terminal != null) {
return terminal;
}
terminal = new PcScTerminal(contextId,name);
terminals.put(name, new WeakReference<GenericTerminal>(terminal));
return terminal;
}
}
/* static long SCardEstablishContext(int scope) throws PcScException {
NativeLongByReference phContext = new NativeLongByReference();
phContext.setValue(new NativeLong((long) 0));
NativeLong status = win32PCSC.SCardEstablishContext(scope, Pointer.NULL, Pointer.NULL, phContext);
if (0 != status.longValue()) {
throw new PcScException(status.intValue());
}
return phContext.getValue().longValue();
}*/
static long SCardConnect(long contextId, String readerName, int shareMode, int preferredProtocols) throws PcScException {
NativeLongByReference phCard = new NativeLongByReference();
IntByReference pdwActiveProtocol = new IntByReference();
NativeLong status = win32PCSC.SCardConnectW(new NativeLong(contextId), new WString(readerName), shareMode, preferredProtocols,
phCard,
pdwActiveProtocol);
if (SCARD_S_SUCCESS != status.longValue()) {
throw new PcScException(status.intValue());
}
return phCard.getValue().longValue();
}
static long SCardReconnect(long cardId, int shareMode, int preferredProtocols, int activationPolicy) throws PcScException {
NativeLongByReference phCard = new NativeLongByReference();
IntByReference pdwActiveProtocol = new IntByReference();
NativeLong status = win32PCSC.SCardReconnect(new NativeLong(cardId), shareMode, preferredProtocols,
activationPolicy,
pdwActiveProtocol);
if (SCARD_S_SUCCESS != status.longValue()) {
throw new PcScException(status.intValue());
}
return phCard.getValue().longValue();
}
static byte[] SCardTransmit(long cardId, int protocol, byte[] buf, int ofs, int len) throws PcScException {
Win32PCSC.SCARD_IO_REQUEST pioSendPci = new Win32PCSC.SCARD_IO_REQUEST();
pioSendPci.dwProtocol = protocol;
pioSendPci.cbPciLength = 8;
IntByReference pcbRecvLength = new IntByReference();
byte pbRecvBuffer[] = new byte[65536 + 16];
pcbRecvLength.setValue(65536 + 16);
if (0 != ofs) {
byte temp[] = new byte[len];
for (int i = 0; i < len; i++) {
temp[i] = buf[ofs + i];
}
buf = temp;
}
NativeLong callStatus = win32PCSC.SCardTransmit(new NativeLong(cardId),
pioSendPci,
buf, len,
(Win32PCSC.SCARD_IO_REQUEST) null,
pbRecvBuffer, pcbRecvLength);
if (SCARD_S_SUCCESS != callStatus.longValue()) {
throw new PcScException("buf.length="+buf.length+", len="+len,callStatus.intValue());
}
byte out[] = new byte[pcbRecvLength.getValue()];
for (int i = 0; i < pcbRecvLength.getValue(); i++) {
out[i] = pbRecvBuffer[i];
}
return out;
}
static byte[] SCardGetAttrib(long cardId, int dwAttrId) throws PcScException {
IntByReference pcbRecvLength = new IntByReference();
byte pbRecvBuffer[] = new byte[200];
pcbRecvLength.setValue(200);
NativeLong callStatus = win32PCSC.SCardGetAttrib(new NativeLong(cardId),
dwAttrId,
pbRecvBuffer /*null*/, pcbRecvLength);
if (SCARD_S_SUCCESS != callStatus.longValue()) {
/*if (ERROR_NOT_SUPPORTED != callStatus.longValue()) {
throw new PcScException("win32PCSC.SCardGetAttrib: attribute not supported",callStatus.intValue());
}*/
throw new PcScException("win32PCSC.SCardGetAttrib",callStatus.intValue());
}
byte out[] = new byte[pcbRecvLength.getValue()];
for (int i = 0; i < pcbRecvLength.getValue(); i++) {
out[i] = pbRecvBuffer[i];
}
return out;
}
// returns the ATR of the card, updates status[] with reader state and protocol
static byte[] SCardStatus(long cardId, byte[] status) throws ScardException {
IntByReference pcchReaderLen = new IntByReference();
IntByReference pdwState = new IntByReference();
IntByReference pdwProtocol = new IntByReference();
byte pbAtr[] = new byte[36];
IntByReference pcbAtrLen = new IntByReference();
pcbAtrLen.setValue(36);
NativeLong callStatus = win32PCSC.SCardStatusW(new NativeLong(cardId), null, pcchReaderLen, pdwState, pdwProtocol, pbAtr, pcbAtrLen);
if (SCARD_S_SUCCESS != callStatus.longValue()) {
throw new PcScException(callStatus.intValue());
}
byte out[] = new byte[pcbAtrLen.getValue()];
for (int i = 0; i < pcbAtrLen.getValue(); i++) {
out[i] = pbAtr[i];
}
status[0]=(byte) pdwState.getValue();
status[1]=(byte) pdwProtocol.getValue();
return out;
}
static void SCardDisconnect(long cardId, int disposition) throws PcScException {
NativeLong status = win32PCSC.SCardDisconnect(new NativeLong(cardId), disposition);
if (SCARD_S_SUCCESS != status.longValue()) {
throw new PcScException(status.intValue());
}
}
static int[] SCardGetStatusChange(long contextId, long timeout, int[] currentState, String[] readerNames) throws ScardException {
Win32PCSC.SCARD_READERSTATE readerStates[] = new Win32PCSC.SCARD_READERSTATE[readerNames.length];
for (int i = 0; i < readerNames.length; i++) {
readerStates[i] = new Win32PCSC.SCARD_READERSTATE();
readerStates[i].szReader = new WString(readerNames[i]);
readerStates[i].pvUserData = Pointer.NULL;
readerStates[i].dwCurrentState = currentState[i];
readerStates[i].dwEventState = SCARD_STATE_UNAWARE;
readerStates[i].cbAtr = 0;
readerStates[i].rgbAtr = new byte[36];
}
NativeLong status = win32PCSC.SCardGetStatusChangeW(new NativeLong(contextId), (int) timeout, readerStates, readerNames.length);
if (SCARD_S_SUCCESS != status.longValue()) {
throw new PcScException(status.intValue());
}
int out[] = new int[readerNames.length];
for (int i = 0; i < readerNames.length; i++) {
out[i] = readerStates[i].dwEventState;
}
return out;
}
final static int SCARD_S_SUCCESS = 0x00000000;
final static int SCARD_E_CANCELLED = 0x80100002;
final static int SCARD_E_CANT_DISPOSE = 0x8010000E;
final static int SCARD_E_INSUFFICIENT_BUFFER = 0x80100008;
final static int SCARD_E_INVALID_ATR = 0x80100015;
final static int SCARD_E_INVALID_HANDLE = 0x80100003;
final static int SCARD_E_INVALID_PARAMETER = 0x80100004;
final static int SCARD_E_INVALID_TARGET = 0x80100005;
final static int SCARD_E_INVALID_VALUE = 0x80100011;
final static int SCARD_E_NO_MEMORY = 0x80100006;
final static int SCARD_F_COMM_ERROR = 0x80100013;
final static int SCARD_F_INTERNAL_ERROR = 0x80100001;
final static int SCARD_F_UNKNOWN_ERROR = 0x80100014;
final static int SCARD_F_WAITED_TOO_LONG = 0x80100007;
final static int SCARD_E_UNKNOWN_READER = 0x80100009;
final static int SCARD_E_TIMEOUT = 0x8010000A;
final static int SCARD_E_SHARING_VIOLATION = 0x8010000B;
final static int SCARD_E_NO_SMARTCARD = 0x8010000C;
final static int SCARD_E_UNKNOWN_CARD = 0x8010000D;
final static int SCARD_E_PROTO_MISMATCH = 0x8010000F;
final static int SCARD_E_NOT_READY = 0x80100010;
final static int SCARD_E_SYSTEM_CANCELLED = 0x80100012;
final static int SCARD_E_NOT_TRANSACTED = 0x80100016;
final static int SCARD_E_READER_UNAVAILABLE = 0x80100017;
final static int SCARD_W_UNSUPPORTED_CARD = 0x80100065;
final static int SCARD_W_UNRESPONSIVE_CARD = 0x80100066;
final static int SCARD_W_UNPOWERED_CARD = 0x80100067;
final static int SCARD_W_RESET_CARD = 0x80100068;
final static int SCARD_W_REMOVED_CARD = 0x80100069;
final static int SCARD_W_INSERTED_CARD = 0x8010006A;
final static int SCARD_E_UNSUPPORTED_FEATURE = 0x8010001F;
final static int SCARD_E_PCI_TOO_SMALL = 0x80100019;
final static int SCARD_E_READER_UNSUPPORTED = 0x8010001A;
final static int SCARD_E_DUPLICATE_READER = 0x8010001B;
final static int SCARD_E_CARD_UNSUPPORTED = 0x8010001C;
final static int SCARD_E_NO_SERVICE = 0x8010001D;
final static int SCARD_E_SERVICE_STOPPED = 0x8010001E;
// MS undocumented
final static int SCARD_E_NO_READERS_AVAILABLE = 0x8010002E;
// std. Windows invalid handle return code, used instead of SCARD code
final static int WINDOWS_ERROR_INVALID_HANDLE = 6;
final static int WINDOWS_ERROR_INVALID_PARAMETER = 87;
final static int SCARD_LEAVE_CARD = 0x0000;
final static int SCARD_RESET_CARD = 0x0001;
final static int SCARD_UNPOWER_CARD = 0x0002;
final static int SCARD_EJECT_CARD = 0x0003;
final static int SCARD_SCOPE_USER = 0x0000;
final static int SCARD_SCOPE_TERMINAL = 0x0001;
final static int SCARD_SCOPE_SYSTEM = 0x0002;
final static int SCARD_SCOPE_GLOBAL = 0x0003;
final static int SCARD_SHARE_EXCLUSIVE = 0x0001;
final static int SCARD_SHARE_SHARED = 0x0002;
final static int SCARD_SHARE_DIRECT = 0x0003;
final static int SCARD_STATE_UNAWARE = 0x0000;
final static int SCARD_STATE_IGNORE = 0x0001;
final static int SCARD_STATE_CHANGED = 0x0002;
final static int SCARD_STATE_UNKNOWN = 0x0004;
final static int SCARD_STATE_UNAVAILABLE = 0x0008;
final static int SCARD_STATE_EMPTY = 0x0010;
final static int SCARD_STATE_PRESENT = 0x0020;
final static int SCARD_STATE_ATRMATCH = 0x0040;
final static int SCARD_STATE_EXCLUSIVE = 0x0080;
final static int SCARD_STATE_INUSE = 0x0100;
final static int SCARD_STATE_MUTE = 0x0200;
final static int SCARD_STATE_UNPOWERED = 0x0400;
final static int TIMEOUT_INFINITE = 0xffffffff;
final static int SCARD_AUTOALLOCATE = -1;
final static int SCARD_PROTOCOL_T0 = 0x0001;
final static int SCARD_PROTOCOL_T1 = 0x0002;
final static int SCARD_PROTOCOL_RAW = 0x10000;
final static int SCARD_UNKNOWN = 0x0000;
final static int SCARD_ABSENT = 0x0001;
final static int SCARD_PRESENT = 0x0002;
final static int SCARD_SWALLOWED = 0x0003;
final static int SCARD_POWERED = 0x0004;
final static int SCARD_NEGOTIABLE = 0x0005;
final static int SCARD_SPECIFIC = 0x0006;
//final static int ERROR_NOT_SUPPORTED = 0x32;
}