/*
* The MIT License
*
* Copyright 2014, 2015, 2016 Rui Martinho (rmartinho@gmail.com), António Braz (antoniocbraz@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.poreid;
import org.poreid.pcscforjava.Card;
import org.poreid.pcscforjava.CardException;
import org.poreid.pcscforjava.CardTerminal;
import org.poreid.pcscforjava.CardTerminals;
import org.poreid.pcscforjava.TerminalFactory;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.poreid.common.Util;
import org.poreid.config.POReIDConfig;
import org.poreid.dialogs.pindialogs.usepinpad.UsePinPadDialogController;
import org.poreid.dialogs.pindialogs.verifypin.VerifyPinDialogController;
import org.poreid.dialogs.selectcard.CanceledSelectionException;
import org.poreid.dialogs.selectcard.SelectCardDialogController;
/**
* Responsável pela instanciação da classe relativa ao tipo de cartão.
* @author POReID
*/
public final class CardFactory {
private static final Locale defaultLocale;
static {
defaultLocale = POReIDConfig.getDefaultLocale();
}
/**
* Obter um cartão e utilizar as parametrizações (linguagem e utilização da cache) definidas no ficheiro de configuração.
* @param <T> Uma classe que implemente a interface POReIDSmartCard
* @return cartão suportado pelo poreid
* @throws CardTerminalNotPresentException Exceção lançada quando não existe um leitor de cartões no sistema
* @throws UnknownCardException Exceção lançada quando o cartão não é reconhecido
* @throws CardNotPresentException Exceção lançada quando não existe um cartão no leitor
* @throws CanceledSelectionException Exceção lançada quando o utilizador não selecionou um de entre os vários cartões que foram detetados
* @throws POReIDException Exceção lançada quando ocorre uma exceção num componente (encapsula a exeção original)
*/
public static <T extends POReIDSmartCard> T getCard() throws CardTerminalNotPresentException, UnknownCardException, CardNotPresentException, CanceledSelectionException, POReIDException{
return getCard(defaultLocale, CacheStatus.UNSET, Proxy.NO_PROXY);
}
/**
* Obter um cartão e utilizar as parametrizações (linguagem e utilização da cache) definidas no ficheiro de configuração.
* @param <T> Uma classe que implemente a interface POReIDSmartCard
* @param proxy Permite indicar um proxy
* @return cartão suportado pelo poreid
* @throws CardTerminalNotPresentException Exceção lançada quando não existe um leitor de cartões no sistema
* @throws UnknownCardException Exceção lançada quando o cartão não é reconhecido
* @throws CardNotPresentException Exceção lançada quando não existe um cartão no leitor
* @throws CanceledSelectionException Exceção lançada quando o utilizador não selecionou um de entre os vários cartões que foram detetados
* @throws POReIDException Exceção lançada quando ocorre uma exceção num componente (encapsula a exeção original)
*/
public static <T extends POReIDSmartCard> T getCard(Proxy proxy) throws CardTerminalNotPresentException, UnknownCardException, CardNotPresentException, CanceledSelectionException, POReIDException{
return getCard(defaultLocale, CacheStatus.UNSET, proxy);
}
/**
* Obter um cartão, redefinir o comportamento da cache (ligada / desligada) e utilizar a parametrização relativa à linguagem definida no ficheiro de configuração.
* @param <T> Uma classe que implemente a interface POReIDSmartCard
* @param cachePreferences Permite indicar se a cache deve ser utilizada
* @return cartão suportado pelo poreid
* @throws CardTerminalNotPresentException Exceção lançada quando não existe um leitor de cartões no sistema
* @throws UnknownCardException Exceção lançada quando o cartão não é reconhecido
* @throws CardNotPresentException Exceção lançada quando não existe um cartão no leitor
* @throws CanceledSelectionException Exceção lançada quando o utilizador não selecionou um de entre os vários cartões que foram detetados
* @throws POReIDException Exceção lançada quando ocorre uma exceção num componente (encapsula a exeção original)
*/
public static <T extends POReIDSmartCard> T getCard(boolean cachePreferences) throws CardTerminalNotPresentException, UnknownCardException, CardNotPresentException, CanceledSelectionException, POReIDException{
return getCard(defaultLocale, CacheStatus.getStatus(cachePreferences), Proxy.NO_PROXY);
}
/**
* Obter um cartão, redefinir o comportamento da cache (ligada / desligada) e utilizar a parametrização relativa à linguagem definida no ficheiro de configuração.
* @param <T> Uma classe que implemente a interface POReIDSmartCard
* @param cachePreferences Permite indicar se a cache deve ser utilizada
* @param proxy Permite indicar um proxy
* @return cartão suportado pelo poreid
* @throws CardTerminalNotPresentException Exceção lançada quando não existe um leitor de cartões no sistema
* @throws UnknownCardException Exceção lançada quando o cartão não é reconhecido
* @throws CardNotPresentException Exceção lançada quando não existe um cartão no leitor
* @throws CanceledSelectionException Exceção lançada quando o utilizador não selecionou um de entre os vários cartões que foram detetados
* @throws POReIDException Exceção lançada quando ocorre uma exceção num componente (encapsula a exeção original)
*/
public static <T extends POReIDSmartCard> T getCard(boolean cachePreferences, Proxy proxy) throws CardTerminalNotPresentException, UnknownCardException, CardNotPresentException, CanceledSelectionException, POReIDException{
return getCard(defaultLocale, CacheStatus.getStatus(cachePreferences), proxy);
}
/**
* Obter um cartão, redefinir a linguagem e utilizar a parametrização relativa ao estado da cache definida no ficheiro de configuração.
* @param <T> Uma classe que implemente a interface POReIDSmartCard
* @param locale Permite escolher a linguagem utilizada (português/inglês)
* @return cartão suportado pelo poreid
* @throws CardTerminalNotPresentException Exceção lançada quando não existe um leitor de cartões no sistema
* @throws UnknownCardException Exceção lançada quando o cartão não é reconhecido
* @throws CardNotPresentException Exceção lançada quando não existe um cartão no leitor
* @throws CanceledSelectionException Exceção lançada quando o utilizador não selecionou um de entre os vários cartões que foram detetados
* @throws POReIDException Exceção lançada quando ocorre uma exceção num componente (encapsula a exeção original)
*/
public static <T extends POReIDSmartCard> T getCard(Locale locale) throws CardTerminalNotPresentException, UnknownCardException, CardNotPresentException, CanceledSelectionException, POReIDException{
if (null == locale){
locale = defaultLocale;
}
return getCard(locale, CacheStatus.UNSET, Proxy.NO_PROXY);
}
/**
* Obter um cartão, redefinir a linguagem e utilizar a parametrização relativa ao estado da cache definida no ficheiro de configuração.
* @param <T> Uma classe que implemente a interface POReIDSmartCard
* @param locale Permite escolher a linguagem utilizada (português/inglês)
* @param proxy Permite indicar um proxy
* @return cartão suportado pelo poreid
* @throws CardTerminalNotPresentException Exceção lançada quando não existe um leitor de cartões no sistema
* @throws UnknownCardException Exceção lançada quando o cartão não é reconhecido
* @throws CardNotPresentException Exceção lançada quando não existe um cartão no leitor
* @throws CanceledSelectionException Exceção lançada quando o utilizador não selecionou um de entre os vários cartões que foram detetados
* @throws POReIDException Exceção lançada quando ocorre uma exceção num componente (encapsula a exeção original)
*/
public static <T extends POReIDSmartCard> T getCard(Locale locale, Proxy proxy) throws CardTerminalNotPresentException, UnknownCardException, CardNotPresentException, CanceledSelectionException, POReIDException{
if (null == locale){
locale = defaultLocale;
}
return getCard(locale, CacheStatus.UNSET, proxy);
}
/**
* Obter um cartão, redefinir o comportamento da cache e da linguagem.
* @param <T> Uma classe que implemente a interface POReIDSmartCard
* @param locale Permite escolher a linguagem utilizada (português/inglês)
* @param cachePreferences Permite indicar se a cache deve ser utilizada
* @return cartão suportado pelo poreid
* @throws CardTerminalNotPresentException Exceção lançada quando não existe um leitor de cartões no sistema
* @throws UnknownCardException Exceção lançada quando o cartão não é reconhecido
* @throws CardNotPresentException Exceção lançada quando não existe um cartão no leitor
* @throws CanceledSelectionException Exceção lançada quando o utilizador não selecionou um de entre os vários cartões que foram detetados
* @throws POReIDException Exceção lançada quando ocorre uma exceção num componente (encapsula a exeção original)
*/
public static <T extends POReIDSmartCard> T getCard(Locale locale, CacheStatus cachePreferences) throws CardTerminalNotPresentException, UnknownCardException, CardNotPresentException, CanceledSelectionException, POReIDException{
return getCard(locale, cachePreferences, Proxy.NO_PROXY);
}
/**
* Obter um cartão, redefinir o comportamento da cache e da linguagem.
* @param <T> Uma classe que implemente a interface POReIDSmartCard
* @param locale Permite escolher a linguagem utilizada (português/inglês)
* @param cachePreferences Permite indicar se a cache deve ser utilizada
* @param proxy Permite indicar um proxy
* @return cartão suportado pelo poreid
* @throws CardTerminalNotPresentException Exceção lançada quando não existe um leitor de cartões no sistema
* @throws UnknownCardException Exceção lançada quando o cartão não é reconhecido
* @throws CardNotPresentException Exceção lançada quando não existe um cartão no leitor
* @throws CanceledSelectionException Exceção lançada quando o utilizador não selecionou um de entre os vários cartões que foram detetados
* @throws POReIDException Exceção lançada quando ocorre uma exceção num componente (encapsula a exeção original)
*/
public static <T extends POReIDSmartCard> T getCard(Locale locale, CacheStatus cachePreferences, Proxy proxy) throws CardTerminalNotPresentException, UnknownCardException, CardNotPresentException, CanceledSelectionException, POReIDException{
TerminalFactory factory;
Iterator<CardTerminal> iterator;
List<CardTerminal> terminals;
List<T> cardList = new ArrayList<>();
boolean unknownCard = false;
T t;
if (javax.swing.SwingUtilities.isEventDispatchThread()) {
throw new POReIDException("Não deve utilizar a Event Dispatch Thread (EDT) para executar lógica da aplicação");
}
try {
factory = TerminalFactory.getDefault();
if (!factory.terminals().isValidContext()) {
factory.releaseContext();
factory = TerminalFactory.getDefault();
}
} catch (CardException ex){
throw new CardTerminalNotPresentException("Não foi encontrado um leitor de cartões",ex);
}
try {
if (factory.terminals().list().isEmpty()) {
factory.releaseContext();
throw new CardTerminalNotPresentException("Não foi encontrado um leitor de cartões");
}
terminals = factory.terminals().list(CardTerminals.State.CARD_PRESENT);
} catch (CardException | NullPointerException ex) {
factory.releaseContext();
throw new CardTerminalNotPresentException("Não foi possível obter lista de leitores", ex);
}
iterator = terminals.iterator();
while (iterator.hasNext()) {
try {
t = knownATR(iterator.next(),locale, cachePreferences, proxy);
cardList.add(t);
} catch (UnknownCardException ex) {
unknownCard = true;
} catch (CardException ignored) { /* trata-se no switch */ }
}
switch (cardList.size()) {
case 0:
if (unknownCard) {
throw new UnknownCardException("Cartão não suportado");
}
throw new CardNotPresentException("Verifique se o cartão está no leitor");
case 1:
return cardList.get(0);
default:
return SelectCardDialogController.getInstance(cardList, locale).selectCard(new Date());
}
}
@SuppressWarnings("unchecked")
private static <T extends POReIDSmartCard> T knownATR(CardTerminal terminal, Locale locale, CacheStatus status, Proxy proxy) throws CardException, UnknownCardException{
Card card = terminal.connect("*");
String className = POReIDConfig.getSmartCardImplementingClassName(Util.bytesToHex(card.getATR().getBytes()));
boolean cachePreferences = POReIDConfig.getSmartCardCacheStatus(Util.bytesToHex(card.getATR().getBytes()));
if (!CacheStatus.isUnset(status)){
cachePreferences = CacheStatus.getStatus(status);
}
if (null != className){
try {
Constructor<? extends POReIDSmartCard> ctor = Class.forName(className).asSubclass(POReIDSmartCard.class).getDeclaredConstructor(Card.class, CardTerminal.class, Locale.class, boolean.class, Proxy.class, Date.class);
return (T) ctor.newInstance(card, terminal, locale, cachePreferences, proxy, new Date());
} catch (InvocationTargetException | IllegalArgumentException | SecurityException | NoSuchMethodException | InstantiationException | IllegalAccessException | ClassNotFoundException ex) {
Logger.getLogger(CardFactory.class.getName()).log(Level.SEVERE, null, ex);
throw new UnknownCardException("Cartão não suportado", ex);
}
}
throw new UnknownCardException("Cartão não suportado");
}
/**
* Possibilita a exibição de uma mensagem nas janelas de diálogo de pedido de PIN - Típicamente informação de contexto
* @param infoMessage - Mensagem a ser exibida, possivel utilizar tags html strong e em
*/
public static void setPinDialogsInfoMessage(String infoMessage){
UsePinPadDialogController.setInfoMessage(infoMessage);
VerifyPinDialogController.setInfoMessage(infoMessage);
}
/**
* Remove a mensagem préviamente passada
*/
public static void removePinDialogsInfoMessage(){
UsePinPadDialogController.removeInfoMessage();
VerifyPinDialogController.removeInfoMessage();
}
/**
* Permite redefinir a utilização da cache face à predefinição existente.
*/
public enum CacheStatus {
/**
* Utilizar e criar cache
*/
ENABLED,
/**
* Não utilizar e criar cache
*/
DISABLED,
/**
* Utilizar a parametrização existente no ficheiro de configuração
*/
UNSET;
static CacheStatus getStatus(boolean status) {
return (status) ? ENABLED : DISABLED;
}
static boolean getStatus(CacheStatus status){
return (ENABLED == status);
}
static boolean isUnset(CacheStatus status){
return (UNSET == status);
}
};
}