summaryrefslogtreecommitdiff
path: root/smcc/src/main/java/at/gv/egiz/smcc/util
diff options
context:
space:
mode:
Diffstat (limited to 'smcc/src/main/java/at/gv/egiz/smcc/util')
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java368
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java150
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java204
-rw-r--r--smcc/src/main/java/at/gv/egiz/smcc/util/TransparentFileInputStream.java194
4 files changed, 916 insertions, 0 deletions
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java b/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java
new file mode 100644
index 00000000..fcd0b876
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/util/ISO7816Utils.java
@@ -0,0 +1,368 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package at.gv.egiz.smcc.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+
+import javax.smartcardio.CardChannel;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+import at.gv.egiz.smcc.ChangeReferenceDataAPDUSpec;
+import at.gv.egiz.smcc.NewReferenceDataAPDUSpec;
+import at.gv.egiz.smcc.SecurityStatusNotSatisfiedException;
+import at.gv.egiz.smcc.SignatureCardException;
+import at.gv.egiz.smcc.VerifyAPDUSpec;
+
+public class ISO7816Utils {
+
+ public static TransparentFileInputStream openTransparentFileInputStream(
+ final CardChannel channel, int maxSize) {
+
+ TransparentFileInputStream file = new TransparentFileInputStream(maxSize) {
+
+ @Override
+ protected byte[] readBinary(int offset, int len) throws IOException {
+
+ ResponseAPDU resp;
+ try {
+ resp = channel.transmit(new CommandAPDU(0x00, 0xB0,
+ 0x7F & (offset >> 8), offset & 0xFF, len));
+ } catch (CardException e) {
+ throw new IOException(e);
+ }
+
+ Throwable cause;
+ if (resp.getSW() == 0x9000) {
+ return resp.getData();
+ } else if (resp.getSW() == 0x6982) {
+ cause = new SecurityStatusNotSatisfiedException();
+ } else {
+ cause = new SignatureCardException("Failed to read bytes (offset=" + offset + ",len="
+ + len + ") SW=" + Integer.toHexString(resp.getSW()) + ".");
+ }
+ throw new IOException(cause);
+
+ }
+
+ };
+
+ return file;
+
+ }
+
+ public static byte[] readTransparentFile(CardChannel channel, int maxSize)
+ throws CardException, SignatureCardException {
+
+ TransparentFileInputStream is = openTransparentFileInputStream(channel, maxSize);
+
+ try {
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ int len;
+ for (byte[] b = new byte[256]; (len = is.read(b)) != -1;) {
+ os.write(b, 0, len);
+ }
+
+ return os.toByteArray();
+
+ } catch (IOException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof CardException) {
+ throw (CardException) cause;
+ }
+ if (cause instanceof SignatureCardException) {
+ throw (SignatureCardException) cause;
+ }
+ throw new SignatureCardException(e);
+ }
+
+ }
+
+ public static byte[] readTransparentFileTLV(CardChannel channel, int maxSize,
+ byte expectedType) throws CardException, SignatureCardException {
+
+ TransparentFileInputStream is = openTransparentFileInputStream(channel,
+ maxSize);
+
+ return readTransparentFileTLV(is, maxSize, expectedType);
+
+ }
+
+ public static byte[] readTransparentFileTLV(TransparentFileInputStream is, int maxSize,
+ byte expectedType) throws CardException, SignatureCardException {
+
+
+ try {
+
+ is.mark(256);
+
+ // check expected type
+ int b = is.read();
+ if (b == 0x00) {
+ return null;
+ }
+ if (b == -1 || expectedType != (0xFF & b)) {
+ throw new SignatureCardException("Unexpected TLV type. Expected "
+ + Integer.toHexString(expectedType) + " but was "
+ + Integer.toHexString(b) + ".");
+ }
+
+ // get actual length
+ int actualSize = 2;
+ b = is.read();
+ if (b == -1) {
+ return null;
+ } else if ((0x80 & b) > 0) {
+ int octets = (0x0F & b);
+ actualSize += octets;
+ for (int i = 1; i <= octets; i++) {
+ b = is.read();
+ if (b == -1) {
+ return null;
+ }
+ actualSize += (0xFF & b) << ((octets - i) * 8);
+ }
+ } else {
+ actualSize += 0xFF & b;
+ }
+
+ // set limit to actual size and read into buffer
+ is.reset();
+ is.setLimit(actualSize);
+ byte[] buf = new byte[actualSize];
+ if (is.read(buf) == actualSize) {
+ return buf;
+ } else {
+ return null;
+ }
+
+ } catch (IOException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof CardException) {
+ throw (CardException) cause;
+ }
+ if (cause instanceof SignatureCardException) {
+ throw (SignatureCardException) cause;
+ }
+ throw new SignatureCardException(e);
+ }
+
+ }
+
+ public static int getLengthFromFCx(byte[] fcx) {
+
+ int len = -1;
+
+ if (fcx.length != 0 && (fcx[0] == (byte) 0x62 || fcx[0] == (byte) 0x6F)) {
+ int pos = 2;
+ while (pos < (fcx[1] - 2)) {
+ switch (fcx[pos]) {
+
+ case (byte) 0x80:
+ case (byte) 0x81: {
+ len = 0xFF & fcx[pos + 2];
+ for (int i = 1; i < fcx[pos + 1]; i++) {
+ len<<=8;
+ len+=0xFF & fcx[pos + i + 2];
+ }
+ }
+
+ default:
+ pos += 0xFF & fcx[pos + 1] + 2;
+ }
+ }
+ }
+
+ return len;
+
+ }
+
+ public static byte[] readRecord(CardChannel channel, int record) throws CardException, SignatureCardException {
+
+ ResponseAPDU resp = channel.transmit(
+ new CommandAPDU(0x00, 0xB2, record, 0x04, 256));
+ if (resp.getSW() == 0x9000) {
+ return resp.getData();
+ } else {
+ throw new SignatureCardException("Failed to read records. SW="
+ + Integer.toHexString(resp.getSW()));
+ }
+
+ }
+
+ public static void formatPIN(int pinFormat, int pinJustification, byte[] fpin, byte[] mask, char[] pin) {
+
+ boolean left = (pinJustification == VerifyAPDUSpec.PIN_JUSTIFICATION_LEFT);
+
+ int j = (left) ? 0 : fpin.length - 1;
+ int step = (left) ? 1 : - 1;
+ switch (pinFormat) {
+ case VerifyAPDUSpec.PIN_FORMAT_BINARY:
+ if (fpin.length < pin.length) {
+ throw new IllegalArgumentException();
+ }
+ for (int i = 0; i < pin.length; i++) {
+ fpin[j] = (byte) Character.digit(pin[i], 10);
+ mask[j] = (byte) 0xFF;
+ j += step;
+ }
+ break;
+
+ case VerifyAPDUSpec.PIN_FORMAT_BCD:
+ if (fpin.length * 2 < pin.length) {
+ throw new IllegalArgumentException();
+ }
+ for (int i = 0; i < pin.length; i++) {
+ int digit = Character.digit(pin[i], 10);
+ boolean h = (i % 2 == 0) ^ left;
+ fpin[j] |= h ? digit : digit << 4 ;
+ mask[j] |= h ? (byte) 0x0F : (byte) 0xF0;
+ j += (i % 2) * step;
+ }
+ break;
+
+ case VerifyAPDUSpec.PIN_FORMAT_ASCII:
+ if (fpin.length < pin.length) {
+ throw new IllegalArgumentException();
+ }
+ byte[] asciiPin = Charset.forName("ASCII").encode(CharBuffer.wrap(pin)).array();
+ for (int i = 0; i < pin.length; i++) {
+ fpin[j] = asciiPin[i];
+ mask[j] = (byte) 0xFF;
+ j += step;
+ }
+ break;
+ }
+
+ }
+
+ public static void insertPIN(byte[] apdu, int pos, byte[] fpin, byte[] mask) {
+ for (int i = 0; i < fpin.length; i++) {
+ apdu[pos + i] &= ~mask[i];
+ apdu[pos + i] |= fpin[i];
+ }
+ }
+
+ public static void insertPINLength(byte[] apdu, int length, int lengthSize, int pos, int offset) {
+
+ // use short (2 byte) to be able to shift the pin length
+ // by the number of bits given by the pin length position
+ short size = (short) (0x00FF & length);
+ short sMask = (short) ((1 << lengthSize) - 1);
+ // shift to the proper position
+ int shift = 16 - lengthSize - (pos % 8);
+ offset += (pos / 8) + 5;
+ size <<= shift;
+ sMask <<= shift;
+ // insert upper byte
+ apdu[offset] &= (0xFF & (~sMask >> 8));
+ apdu[offset] |= (0xFF & (size >> 8));
+ // insert lower byte
+ apdu[offset + 1] &= (0xFF & ~sMask);
+ apdu[offset + 1] |= (0xFF & size);
+
+ }
+
+ public static CommandAPDU createVerifyAPDU(VerifyAPDUSpec apduSpec, char[] pin) {
+
+ // format pin
+ byte[] fpin = new byte[apduSpec.getPinLength()];
+ byte[] mask = new byte[apduSpec.getPinLength()];
+ formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), fpin, mask, pin);
+
+ byte[] apdu = apduSpec.getApdu();
+
+ // insert formated pin
+ insertPIN(apdu, apduSpec.getPinPosition() + 5, fpin, mask);
+
+ // insert pin length
+ if (apduSpec.getPinLengthSize() != 0) {
+ insertPINLength(apdu, pin.length, apduSpec.getPinLengthSize(), apduSpec.getPinLengthPos(), 0);
+ }
+
+ return new CommandAPDU(apdu);
+
+ }
+
+ public static CommandAPDU createChangeReferenceDataAPDU(
+ ChangeReferenceDataAPDUSpec apduSpec, char[] oldPin, char[] newPin) {
+
+ // format old pin
+ byte[] fpin = new byte[apduSpec.getPinLength()];
+ byte[] mask = new byte[apduSpec.getPinLength()];
+ formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), fpin, mask, oldPin);
+
+ byte[] apdu = apduSpec.getApdu();
+
+ // insert formated old pin
+ insertPIN(apdu, apduSpec.getPinPosition() + apduSpec.getPinInsertionOffsetOld() + 5, fpin, mask);
+
+ // insert pin length
+ if (apduSpec.getPinLengthSize() != 0) {
+ insertPINLength(apdu, oldPin.length, apduSpec.getPinLengthSize(),
+ apduSpec.getPinLengthPos(), apduSpec.getPinInsertionOffsetOld());
+ }
+
+ // format new pin
+ fpin = new byte[apduSpec.getPinLength()];
+ mask = new byte[apduSpec.getPinLength()];
+ formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), fpin, mask, newPin);
+
+ // insert formated new pin
+ insertPIN(apdu, apduSpec.getPinPosition() + apduSpec.getPinInsertionOffsetNew() + 5, fpin, mask);
+
+ // insert pin length
+ if (apduSpec.getPinLengthSize() != 0) {
+ insertPINLength(apdu, newPin.length, apduSpec.getPinLengthSize(),
+ apduSpec.getPinLengthPos(), apduSpec.getPinInsertionOffsetNew());
+ }
+
+ return new CommandAPDU(apdu);
+
+ }
+
+ public static CommandAPDU createNewReferenceDataAPDU(
+ NewReferenceDataAPDUSpec apduSpec, char[] newPin) {
+
+ // format old pin
+ byte[] fpin = new byte[apduSpec.getPinLength()];
+ byte[] mask = new byte[apduSpec.getPinLength()];
+ formatPIN(apduSpec.getPinFormat(), apduSpec.getPinJustification(), fpin, mask, newPin);
+
+ byte[] apdu = apduSpec.getApdu();
+
+ // insert formated new pin
+ insertPIN(apdu, apduSpec.getPinPosition() + apduSpec.getPinInsertionOffsetNew() + 5, fpin, mask);
+
+ // insert pin length
+ if (apduSpec.getPinLengthSize() != 0) {
+ insertPINLength(apdu, newPin.length, apduSpec.getPinLengthSize(),
+ apduSpec.getPinLengthPos(), apduSpec.getPinInsertionOffsetNew());
+ }
+
+ return new CommandAPDU(apdu);
+
+ }
+
+
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java b/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java
new file mode 100644
index 00000000..f7d3bab7
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/util/SMCCHelper.java
@@ -0,0 +1,150 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package at.gv.egiz.smcc.util;
+
+import java.util.Locale;
+import java.util.Map;
+
+import javax.smartcardio.ATR;
+import javax.smartcardio.Card;
+import javax.smartcardio.CardTerminal;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import at.gv.egiz.smcc.CardNotSupportedException;
+import at.gv.egiz.smcc.SignatureCard;
+import at.gv.egiz.smcc.SignatureCardFactory;
+
+public class SMCCHelper {
+
+ public final static int NO_CARD = 0;
+ public final static int PC_SC_NOT_SUPPORTED = 1;
+ public final static int TERMINAL_NOT_PRESENT = 2;
+ public final static int CARD_NOT_SUPPORTED = 3;
+ public final static int CARD_FOUND = 4;
+
+ private final static Log log = LogFactory.getLog(SMCCHelper.class);
+
+ protected SmartCardIO smartCardIO = new SmartCardIO();
+ protected int resultCode = NO_CARD;
+ protected SignatureCard signatureCard = null;
+ protected static boolean useSWCard = false;
+
+ public SMCCHelper() {
+ update();
+ }
+
+ public synchronized void update() {
+ update(-1);
+ }
+
+ public synchronized void update(int sleep) {
+ SignatureCardFactory factory = SignatureCardFactory.getInstance();
+ if (useSWCard) {
+ try {
+ signatureCard = factory.createSignatureCard(null, null);
+ resultCode = CARD_FOUND;
+ } catch (CardNotSupportedException e) {
+ resultCode = CARD_NOT_SUPPORTED;
+ signatureCard = null;
+ }
+ return;
+ }
+ signatureCard = null;
+ resultCode = NO_CARD;
+ // find pcsc support
+ if (smartCardIO.isPCSCSupported()) {
+ // find supported card
+ if (smartCardIO.isTerminalPresent()) {
+ Map<CardTerminal, Card> newCards = null;
+ if (sleep > 0) {
+ smartCardIO.waitForInserted(sleep);
+
+ }
+ newCards = smartCardIO.getCards();
+ for (CardTerminal cardTerminal : newCards.keySet()) {
+ try {
+ Card c = newCards.get(cardTerminal);
+ if (c == null) {
+ throw new CardNotSupportedException();
+ }
+ signatureCard = factory.createSignatureCard(c, cardTerminal);
+ ATR atr = newCards.get(cardTerminal).getATR();
+ log.trace("Found supported card (" + signatureCard.toString() + ") "
+ + "in terminal '" + cardTerminal.getName() + "', ATR = "
+ + toString(atr.getBytes()) + ".");
+ resultCode = CARD_FOUND;
+ break;
+
+ } catch (CardNotSupportedException e) {
+ Card c = newCards.get(cardTerminal);
+ if (c != null) {
+ ATR atr = c.getATR();
+ log.info("Found unsupported card" + " in terminal '"
+ + cardTerminal.getName() + "', ATR = "
+ + toString(atr.getBytes()) + ".");
+ } else {
+ log.info("Found unsupported card in terminal '"
+ + cardTerminal.getName() + "' without ATR");
+ }
+ resultCode = CARD_NOT_SUPPORTED;
+ }
+ }
+ } else {
+ resultCode = TERMINAL_NOT_PRESENT;
+ }
+ } else {
+ resultCode = PC_SC_NOT_SUPPORTED;
+ }
+ }
+
+ public synchronized SignatureCard getSignatureCard(Locale locale) {
+ if (signatureCard != null) {
+ signatureCard.setLocale(locale);
+ }
+ return signatureCard;
+ }
+
+ public int getResultCode() {
+ return resultCode;
+ }
+
+ public static String toString(byte[] b) {
+ StringBuffer sb = new StringBuffer();
+ sb.append('[');
+ if (b != null && b.length > 0) {
+ sb.append(Integer.toHexString((b[0] & 240) >> 4));
+ sb.append(Integer.toHexString(b[0] & 15));
+ for (int i = 1; i < b.length; i++) {
+ sb.append((i % 32 == 0) ? '\n' : ':');
+ sb.append(Integer.toHexString((b[i] & 240) >> 4));
+ sb.append(Integer.toHexString(b[i] & 15));
+ }
+ }
+ sb.append(']');
+ return sb.toString();
+ }
+
+ public static boolean isUseSWCard() {
+ return useSWCard;
+ }
+
+ public static void setUseSWCard(boolean useSWCard) {
+ SMCCHelper.useSWCard = useSWCard;
+ }
+}
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java b/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java
new file mode 100644
index 00000000..b1866894
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/util/SmartCardIO.java
@@ -0,0 +1,204 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package at.gv.egiz.smcc.util;
+
+import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.smartcardio.Card;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CardTerminal;
+import javax.smartcardio.CardTerminals;
+import javax.smartcardio.TerminalFactory;
+import javax.smartcardio.CardTerminals.State;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ *
+ * @author mcentner
+ */
+public class SmartCardIO {
+
+ private static final int STATE_INITIALIZED = 1;
+
+ private static final int STATE_TERMINAL_FACTORY = 2;
+
+ private static final int STATE_TERMINALS = 3;
+
+ private static Log log = LogFactory.getLog(SmartCardIO.class);
+
+ final Map<CardTerminal, Card> terminalCard_ = new HashMap<CardTerminal, Card>();
+
+ int state_ = STATE_INITIALIZED;
+
+ TerminalFactory terminalFactory_ = null;
+
+ CardTerminals cardTerminals_;
+
+ private void updateTerminalFactory() {
+ TerminalFactory terminalFactory;
+ try {
+ terminalFactory = TerminalFactory.getInstance("PC/SC", null);
+ } catch (NoSuchAlgorithmException e) {
+ log.info("Failed to get TerminalFactory of type 'PC/SC'.", e);
+ terminalFactory = TerminalFactory.getDefault();
+ }
+ log.debug("TerminalFactory : " + terminalFactory);
+ if ("PC/SC".equals(terminalFactory.getType())) {
+ terminalFactory_ = terminalFactory;
+ }
+ if(state_ < STATE_TERMINAL_FACTORY) {
+ state_ = STATE_TERMINAL_FACTORY;
+ }
+ }
+
+ public boolean isPCSCSupported() {
+ if(state_ < STATE_TERMINAL_FACTORY) {
+ updateTerminalFactory();
+ }
+ return terminalFactory_ != null;
+ }
+
+ private void updateCardTerminals() {
+ if(terminalFactory_ != null) {
+ cardTerminals_ = terminalFactory_.terminals();
+ }
+ log.debug("CardTerminals : " + cardTerminals_);
+ if (state_ < STATE_TERMINALS) {
+ state_ = STATE_TERMINALS;
+ }
+ }
+
+ public CardTerminals getCardTerminals() {
+ if(state_ < STATE_TERMINAL_FACTORY) {
+ updateTerminalFactory();
+ }
+ if(state_ < STATE_TERMINALS) {
+ updateCardTerminals();
+ }
+ return cardTerminals_;
+ }
+
+ public boolean isTerminalPresent() {
+ CardTerminals cardTerminals = getCardTerminals();
+ if (cardTerminals != null) {
+ List<CardTerminal> terminals = null;
+ try {
+ terminals = cardTerminals.list(State.ALL);
+
+ // logging
+ if(log.isInfoEnabled()) {
+ if (terminals == null || terminals.isEmpty()) {
+ log.info("No card terminal found.");
+ } else {
+ StringBuffer msg = new StringBuffer();
+ msg.append("Found " + terminals.size() + " card terminal(s):");
+ for (CardTerminal terminal : terminals) {
+ msg.append("\n " + terminal.getName());
+ }
+ log.info(msg.toString());
+ }
+ }
+
+ return terminals != null && !terminals.isEmpty();
+ } catch (CardException e) {
+ log.info("Failed to list card terminals.", e);
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ private Map<CardTerminal, Card> updateCards() {
+
+ // clear card references if removed
+ try {
+ log.trace("terminals.list(State.CARD_REMOVAL)");
+ for (CardTerminal terminal : cardTerminals_.list(CardTerminals.State.CARD_REMOVAL)) {
+ Card card = terminalCard_.remove(terminal);
+ log.trace("card removed : " + card);
+ }
+ } catch (CardException e) {
+ log.debug(e);
+ }
+
+ // check inserted cards
+ Map<CardTerminal, Card> newCards = new HashMap<CardTerminal, Card>();
+ try {
+ log.trace("terminals.list(State.CARD_INSERTION)");
+ for (CardTerminal terminal : cardTerminals_.list(CardTerminals.State.CARD_INSERTION)) {
+
+ Card card = null;
+ try {
+ log.trace("Trying to connect to card.");
+ // try to connect to card
+ card = terminal.connect("*");
+ } catch (CardException e) {
+ log.trace("Failed to connect to card.", e);
+ }
+
+ // have we seen this card before?
+ if (terminalCard_.put(terminal, card) == null) {
+ terminalCard_.put(terminal, card);
+ newCards.put(terminal, card);
+ log.trace("terminal '" + terminal + "' card inserted : " + card);
+ }
+ }
+ } catch (CardException e) {
+ log.debug(e);
+ }
+ return newCards;
+
+ }
+
+ public Map<CardTerminal, Card> getCards() {
+ if(state_ < STATE_TERMINAL_FACTORY) {
+ updateTerminalFactory();
+ }
+ if(state_ < STATE_TERMINALS) {
+ updateCardTerminals();
+ }
+ updateCards();
+ Map<CardTerminal, Card> terminalCard = new HashMap<CardTerminal, Card>();
+ terminalCard.putAll(terminalCard_);
+ return Collections.unmodifiableMap(terminalCard);
+ }
+
+ public Map<CardTerminal, Card> waitForInserted(int timeout) {
+ if(state_ < STATE_TERMINAL_FACTORY) {
+ updateTerminalFactory();
+ }
+ if(state_ < STATE_TERMINALS) {
+ updateCardTerminals();
+ }
+ try {
+ // just waiting for a short period of time to allow for abort
+ cardTerminals_.waitForChange(timeout);
+ } catch (CardException e) {
+ log.debug("CardTerminals.waitForChange(" + timeout + ") failed.", e);
+ }
+ Map<CardTerminal, Card> newCards = new HashMap<CardTerminal, Card>();
+ newCards.putAll(updateCards());
+ return Collections.unmodifiableMap(newCards);
+ }
+} \ No newline at end of file
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/util/TransparentFileInputStream.java b/smcc/src/main/java/at/gv/egiz/smcc/util/TransparentFileInputStream.java
new file mode 100644
index 00000000..781f9137
--- /dev/null
+++ b/smcc/src/main/java/at/gv/egiz/smcc/util/TransparentFileInputStream.java
@@ -0,0 +1,194 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package at.gv.egiz.smcc.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public abstract class TransparentFileInputStream extends InputStream {
+
+ private final int chunkSize = 256;
+
+ private byte[] buf = new byte[chunkSize];
+ private int start = 0;
+ private int end = 0;
+
+ private int offset = 0;
+
+ private int length = -1;
+
+ private int limit = -1;
+
+ private int mark = -1;
+
+ private int readlimit = -1;
+
+ public TransparentFileInputStream() {
+ }
+
+ public TransparentFileInputStream(int length) {
+ this.length = length;
+ }
+
+ public void setLimit(int limit) {
+ this.limit = limit;
+ }
+
+ private int fill() throws IOException {
+ if (start == end && (limit < 0 || offset < limit)) {
+ int l;
+ if (limit > 0 && limit - offset < chunkSize) {
+ l = limit - offset;
+ } else if (length > 0) {
+ if (length - offset < chunkSize) {
+ l = length - offset;
+ } else {
+ l = chunkSize - 1;
+ }
+ } else {
+ l = chunkSize;
+ }
+ byte[] b = readBinary(offset, l);
+ offset += b.length;
+ if (mark < 0) {
+ start = 0;
+ end = b.length;
+ System.arraycopy(b, 0, buf, start, b.length);
+ } else {
+ if (end - mark + b.length > buf.length) {
+ // double buffer size
+ byte[] nbuf = new byte[buf.length * 2];
+ System.arraycopy(buf, mark, nbuf, 0, end - mark);
+ buf = nbuf;
+ } else {
+ System.arraycopy(buf, mark, buf, 0, end - mark);
+ }
+ start = start - mark;
+ end = end - mark + b.length;
+ mark = 0;
+ System.arraycopy(b, 0, buf, start, b.length);
+ }
+ if (l > b.length) {
+ // end of file reached
+ setLimit(offset);
+ }
+ }
+ return end - start;
+ }
+
+ protected abstract byte[] readBinary(int offset, int len) throws IOException;
+
+ @Override
+ public int read() throws IOException {
+ int b = (fill() > 0) ? 0xFF & buf[start++] : -1;
+ if (readlimit > 0 && start > readlimit) {
+ mark = -1;
+ readlimit = -1;
+ }
+ return b;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (b == null) {
+ throw new NullPointerException();
+ } else if (off < 0 || len < 0 || len > b.length - off) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return 0;
+ }
+
+ int count = 0;
+ int l;
+ while (count < len) {
+ if (fill() > 0) {
+ l = Math.min(end - start, len - count);
+ System.arraycopy(buf, start, b, off, l);
+ start += l;
+ off += l;
+ count += l;
+ if (readlimit > 0 && start > readlimit) {
+ mark = -1;
+ readlimit = -1;
+ }
+ } else {
+ return (count > 0) ? count : -1;
+ }
+ }
+
+ return count;
+
+ }
+
+ @Override
+ public synchronized void mark(int readlimit) {
+ this.readlimit = readlimit;
+ mark = start;
+ }
+
+ @Override
+ public boolean markSupported() {
+ return true;
+ }
+
+ @Override
+ public synchronized void reset() throws IOException {
+ if (mark < 0) {
+ throw new IOException();
+ } else {
+ start = mark;
+ }
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+
+ if (n <= 0) {
+ return 0;
+ }
+
+ if (n <= end - start) {
+ start += n;
+ return n;
+ } else {
+
+ mark = -1;
+
+ long remaining = n - (end - start);
+ start = end;
+
+ if (limit >= 0 && limit < offset + remaining) {
+ remaining -= limit - offset;
+ offset = limit;
+ return n - remaining;
+ }
+
+ if (length >= 0 && length < offset + remaining) {
+ remaining -= length - offset;
+ offset = length;
+ return n - remaining;
+ }
+
+ offset += remaining;
+
+ return n;
+
+ }
+
+ }
+
+}