From 890c3c0d321c3b59ec1224d4630b3f2401ef94e7 Mon Sep 17 00:00:00 2001 From: tkellner Date: Tue, 7 Jan 2014 17:28:54 +0000 Subject: Add custom PC/SC library for MacOS X from OpeneCard project git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@1276 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- .../java/org/openecard/scio/osx/PCSCTerminals.java | 290 +++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 smcc/src/main/java/org/openecard/scio/osx/PCSCTerminals.java (limited to 'smcc/src/main/java/org/openecard/scio/osx/PCSCTerminals.java') diff --git a/smcc/src/main/java/org/openecard/scio/osx/PCSCTerminals.java b/smcc/src/main/java/org/openecard/scio/osx/PCSCTerminals.java new file mode 100644 index 00000000..495d0679 --- /dev/null +++ b/smcc/src/main/java/org/openecard/scio/osx/PCSCTerminals.java @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.openecard.scio.osx; + +import static javax.smartcardio.CardTerminals.State.CARD_ABSENT; +import static javax.smartcardio.CardTerminals.State.CARD_INSERTION; +import static javax.smartcardio.CardTerminals.State.CARD_PRESENT; +import static javax.smartcardio.CardTerminals.State.CARD_REMOVAL; +import static org.openecard.scio.osx.PCSC.SCARD_E_TIMEOUT; +import static org.openecard.scio.osx.PCSC.SCARD_SCOPE_USER; +import static org.openecard.scio.osx.PCSC.SCARD_STATE_PRESENT; +import static org.openecard.scio.osx.PCSC.SCARD_STATE_UNAWARE; +import static org.openecard.scio.osx.PCSC.SCardEstablishContext; +import static org.openecard.scio.osx.PCSC.SCardGetStatusChange; +import static org.openecard.scio.osx.PCSC.SCardListReaders; +import static org.openecard.scio.osx.PCSC.TIMEOUT_INFINITE; + +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.smartcardio.CardException; +import javax.smartcardio.CardTerminal; +import javax.smartcardio.CardTerminals; + +/** + * TerminalFactorySpi implementation class. + * + * @since 1.6 + * @author Andreas Sterbenz + */ +final class PCSCTerminals extends CardTerminals { + + // SCARDCONTEXT, currently shared between all threads/terminals + private static long contextId; + + // terminal state used by waitForCard() + private Map stateMap; + + PCSCTerminals() { + // empty + } + + static synchronized void initContext() throws PCSCException { + if (contextId == 0) { + contextId = SCardEstablishContext(SCARD_SCOPE_USER); + } + } + + private static final Map> terminals = new HashMap>(); + + private static synchronized TerminalImpl implGetTerminal(String name) { + Reference ref = terminals.get(name); + TerminalImpl terminal = (ref != null) ? ref.get() : null; + if (terminal != null) { + return terminal; + } + terminal = new TerminalImpl(contextId, name); + terminals.put(name, new WeakReference(terminal)); + return terminal; + + } + + public synchronized List list(State state) throws CardException { + if (state == null) { + throw new NullPointerException(); + } + try { + String[] readerNames = SCardListReaders(contextId); + List list = new ArrayList(readerNames.length); + if (stateMap == null) { + // If waitForChange() has never been called, treat event + // queries as status queries. + if (state == CARD_INSERTION) { + state = CARD_PRESENT; + } else if (state == CARD_REMOVAL) { + state = CARD_ABSENT; + } + } + for (String readerName : readerNames) { + CardTerminal terminal = implGetTerminal(readerName); + ReaderState readerState; + switch (state) { + case ALL: + list.add(terminal); + break; + case CARD_PRESENT: + if (terminal.isCardPresent()) { + list.add(terminal); + } + break; + case CARD_ABSENT: + if (terminal.isCardPresent() == false) { + list.add(terminal); + } + break; + case CARD_INSERTION: + readerState = stateMap.get(readerName); + if ((readerState != null) && readerState.isInsertion()) { + list.add(terminal); + } + break; + case CARD_REMOVAL: + readerState = stateMap.get(readerName); + if ((readerState != null) && readerState.isRemoval()) { + list.add(terminal); + } + break; + default: + throw new CardException("Unknown state: " + state); + } + } + return Collections.unmodifiableList(list); + } catch (PCSCException e) { + throw new CardException("list() failed", e); + } + } + + private static class ReaderState { + private int current, previous; + + ReaderState() { + current = SCARD_STATE_UNAWARE; + previous = SCARD_STATE_UNAWARE; + } + + int get() { + return current; + } + + void update(int newState) { + previous = current; + current = newState; + } + + boolean isInsertion() { + return !present(previous) && present(current); + } + + boolean isRemoval() { + return present(previous) && !present(current); + } + + static boolean present(int state) { + return (state & SCARD_STATE_PRESENT) != 0; + } + } + + public synchronized boolean waitForChange(long timeout) throws CardException { + if (timeout < 0) { + throw new IllegalArgumentException("Timeout must not be negative: " + + timeout); + } + if (stateMap == null) { + // We need to initialize the state database. + // Do that with a recursive call, which will return immediately + // because we pass SCARD_STATE_UNAWARE. + // After that, proceed with the real call. + stateMap = new HashMap(); + waitForChange(0); + } + if (timeout == 0) { + timeout = TIMEOUT_INFINITE; + } + try { + String[] readerNames = SCardListReaders(contextId); + int n = readerNames.length; + if (n == 0) { + throw new IllegalStateException("No terminals available"); + } + int[] status = new int[n]; + ReaderState[] readerStates = new ReaderState[n]; + for (int i = 0; i < readerNames.length; i++) { + String name = readerNames[i]; + ReaderState state = stateMap.get(name); + if (state == null) { + state = new ReaderState(); + } + readerStates[i] = state; + status[i] = state.get(); + } + status = SCardGetStatusChange(contextId, timeout, status, readerNames); + stateMap.clear(); // remove any readers that are no longer available + for (int i = 0; i < n; i++) { + ReaderState state = readerStates[i]; + state.update(status[i]); + stateMap.put(readerNames[i], state); + } + return true; + } catch (PCSCException e) { + if (e.code == SCARD_E_TIMEOUT) { + return false; + } else { + throw new CardException("waitForChange() failed", e); + } + } + } + + static List waitForCards( + List terminals, long timeout, boolean wantPresent) + throws CardException { + // the argument sanity checks are performed in + // javax.smartcardio.TerminalFactory or TerminalImpl + + long thisTimeout; + if (timeout == 0) { + timeout = TIMEOUT_INFINITE; + thisTimeout = TIMEOUT_INFINITE; + } else { + // if timeout is not infinite, do the initial call that retrieves + // the status with a 0 timeout. Otherwise, we might get incorrect + // timeout exceptions (seen on Solaris with PC/SC shim) + thisTimeout = 0; + } + + String[] names = new String[terminals.size()]; + int i = 0; + for (CardTerminal terminal : terminals) { + if (terminal instanceof TerminalImpl == false) { + throw new IllegalArgumentException("Invalid terminal type: " + + terminal.getClass().getName()); + } + TerminalImpl impl = (TerminalImpl) terminal; + names[i++] = impl.name; + } + + int[] status = new int[names.length]; + Arrays.fill(status, SCARD_STATE_UNAWARE); + + try { + while (true) { + // note that we pass "timeout" on each native PC/SC call + // that means that if we end up making multiple (more than 2) + // calls, we might wait too long. + // for now assume that is unlikely and not a problem. + status = SCardGetStatusChange(contextId, thisTimeout, status, names); + thisTimeout = timeout; + + List results = null; + for (i = 0; i < names.length; i++) { + boolean nowPresent = (status[i] & SCARD_STATE_PRESENT) != 0; + if (nowPresent == wantPresent) { + if (results == null) { + results = new ArrayList(); + } + results.add(implGetTerminal(names[i])); + } + } + + if (results != null) { + return Collections.unmodifiableList(results); + } + } + } catch (PCSCException e) { + if (e.code == SCARD_E_TIMEOUT) { + return Collections.emptyList(); + } else { + throw new CardException("waitForCard() failed", e); + } + } + } + +} -- cgit v1.2.3