/****************************************************************************
* Copyright (C) 2012 ecsec GmbH.
* All rights reserved.
* Contact: ecsec GmbH (info@ecsec.de)
*
* This file is part of the Open eCard App.
*
* GNU General Public License Usage
* This file may be used under the terms of the GNU General Public
* License version 3.0 as published by the Free Software Foundation
* and appearing in the file LICENSE.GPL included in the packaging of
* this file. Please review the following information to ensure the
* GNU General Public License version 3.0 requirements will be met:
* http://www.gnu.org/copyleft/gpl.html.
*
* Other Usage
* Alternatively, this file may be used in accordance with the terms
* and conditions contained in a signed written agreement between
* you and ecsec GmbH.
*
***************************************************************************/
package org.openecard.ifd.scio.wrapper;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ConcurrentSkipListSet;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CardTerminals;
import org.openecard.common.ECardConstants;
import org.openecard.ifd.scio.IFDException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Tobias Wich <tobias.wich@ecsec.de>
*/
public class SCWrapper {
private static final Logger _logger = LoggerFactory.getLogger(SCWrapper.class);
private final CardTerminals terminals;
private final SecureRandom secureRandom;
private final ConcurrentSkipListMap<String,SCTerminal> scTerminals;
public SCWrapper() throws IFDException {
terminals = new DeadAndAliveTerminals();
secureRandom = new SecureRandom();
scTerminals = new ConcurrentSkipListMap<String, SCTerminal>();
}
public byte[] createHandle(int size) {
byte[] handle = new byte[size];
secureRandom.nextBytes(handle);
return handle;
}
public synchronized SCChannel getChannel(byte[] handle) throws IFDException {
for (SCTerminal t : getTerminals()) {
if (t.isConnected()) {
SCCard c = t.getCard(); // this may produce a valid error
try {
// try to match handle, error is raised and element skipped if not matching
SCChannel ch = c.getChannel(handle);
return ch;
} catch (IFDException ex) {
// ignore, as this exception belongs to the normal find process
}
}
}
IFDException ex = new IFDException(ECardConstants.Minor.IFD.INVALID_SLOT_HANDLE, "Slot handle does not exist.");
_logger.warn(ex.getMessage(), ex);
throw ex;
}
public synchronized SCCard getCard(byte[] handle) throws IFDException {
for (SCTerminal t : getTerminals()) {
if (t.isConnected()) {
SCCard c = t.getCard();
try {
// try to match handle, error is raised and element skipped if not matching
c.getChannel(handle);
return c;
} catch (IFDException ex) {
// ignore, as this exception belongs to the normal find process
}
}
}
IFDException ex = new IFDException(ECardConstants.Minor.IFD.INVALID_SLOT_HANDLE, "Slot handle does not exist.");
_logger.warn(ex.getMessage(), ex);
throw ex;
}
public SCTerminal getTerminal(byte[] handle) throws IFDException {
for (SCTerminal t : getTerminals()) {
if (t.isConnected()) {
SCCard c = t.getCard();
try {
// try to match handle, error is raised and element skipped if not matching
c.getChannel(handle);
return t;
} catch (IFDException ex) {
// ignore, as this exception belongs to the normal find process
}
}
}
IFDException ex = new IFDException(ECardConstants.Minor.IFD.INVALID_SLOT_HANDLE, "Slot handle does not exist.");
_logger.warn(ex.getMessage(), ex);
throw ex;
}
public synchronized SCTerminal getTerminal(String ifdName) throws IFDException {
SCTerminal t = getTerminal(ifdName, false);
return t;
}
public synchronized SCTerminal getTerminal(String ifdName, boolean update) throws IFDException {
if (update) {
updateTerminals();
}
SCTerminal t = scTerminals.get(ifdName);
if (t == null) {
IFDException ex = new IFDException(ECardConstants.Minor.IFD.UNKNOWN_IFD, "IFD with name '" + ifdName + "' does not exist.");
_logger.warn(ex.getMessage(), ex);
throw ex;
}
return t;
}
public synchronized List<SCTerminal> getTerminals() {
try {
List<SCTerminal> list = getTerminals(false);
return list;
} catch (IFDException ex) { // there is no exception if no update happens
_logger.error(ex.getMessage(), ex);
return null;
}
}
public synchronized List<SCTerminal> getTerminals(boolean update) throws IFDException {
if (update) {
updateTerminals();
}
ArrayList<SCTerminal> list = new ArrayList<SCTerminal>(scTerminals.values());
return list;
}
public synchronized List<String> getTerminalNames() {
try {
List<String> list = getTerminalNames(false);
return list;
} catch (IFDException ex) { // there is no exception if no update happens
_logger.error(ex.getMessage(), ex);
return null;
}
}
public synchronized List<String> getTerminalNames(boolean update) throws IFDException {
if (update) {
updateTerminals();
}
ArrayList<String> list = new ArrayList<String>(scTerminals.keySet());
return list;
}
public synchronized void updateTerminals() {
ConcurrentSkipListSet<String> deleted = new ConcurrentSkipListSet<String>(scTerminals.keySet());
// get list and check all entries
List<CardTerminal> ts;
try {
ts = terminals.list();
} catch (CardException ex) {
ts = new ArrayList<CardTerminal>(0); // empty list because list call can fail with exception on some systems
}
for (CardTerminal t : ts) {
if (scTerminals.containsKey(t.getName())) {
// remove from deleted list
deleted.remove(t.getName());
// update terminal status
scTerminals.get(t.getName()).updateTerminal();
} else {
// add new terminal to list
SCTerminal newTerm = new SCTerminal(t, this);
scTerminals.put(t.getName(), newTerm);
}
}
// remove deleted terminals from map
for (String s : deleted) {
scTerminals.remove(s);
}
}
public synchronized boolean waitForChange(long timeout) throws IFDException {
try {
return terminals.waitForChange(timeout);
} catch (CardException ex) {
throw new IFDException(ex);
}
}
public synchronized boolean waitForChange() throws IFDException {
return waitForChange(0);
}
/**
* Try to shut down as much as possible. This may break the whole structure, but it is ok for release context.
*/
public synchronized void releaseAll() {
updateTerminals();
for (SCTerminal t : getTerminals()) {
try {
t.disconnect();
} catch (CardException ex) {
}
}
}
}