From 22001c93bca360d1b15c252cb22d2a4147ff350d Mon Sep 17 00:00:00 2001
From: clemenso <clemenso@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4>
Date: Thu, 20 Aug 2009 16:24:55 +0000
Subject:  [#430] Activation/PIN-management in MOCCA Web Start - new Modules:
 smccSTALExt, BKUGuiExt in order not to depend on BKUAppletExt in BKULocal -
 provide stal-request handler de-registration in abstractSMCCSTAL

git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@448 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4
---
 .../egiz/bku/smccstal/CardMgmtRequestHandler.java  | 177 ++++++++++++++
 .../egiz/bku/smccstal/GetPINStatusException.java   |  41 ++++
 .../bku/smccstal/ManagementPINProviderFactory.java | 262 +++++++++++++++++++++
 .../bku/smccstal/PINManagementRequestHandler.java  | 245 +++++++++++++++++++
 4 files changed, 725 insertions(+)
 create mode 100644 smccSTALExt/src/main/java/at/gv/egiz/bku/smccstal/CardMgmtRequestHandler.java
 create mode 100644 smccSTALExt/src/main/java/at/gv/egiz/bku/smccstal/GetPINStatusException.java
 create mode 100644 smccSTALExt/src/main/java/at/gv/egiz/bku/smccstal/ManagementPINProviderFactory.java
 create mode 100644 smccSTALExt/src/main/java/at/gv/egiz/bku/smccstal/PINManagementRequestHandler.java

(limited to 'smccSTALExt/src/main')

diff --git a/smccSTALExt/src/main/java/at/gv/egiz/bku/smccstal/CardMgmtRequestHandler.java b/smccSTALExt/src/main/java/at/gv/egiz/bku/smccstal/CardMgmtRequestHandler.java
new file mode 100644
index 00000000..533206b3
--- /dev/null
+++ b/smccSTALExt/src/main/java/at/gv/egiz/bku/smccstal/CardMgmtRequestHandler.java
@@ -0,0 +1,177 @@
+/*
+* 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.bku.smccstal;
+
+import at.gv.egiz.bku.gui.ActivationGUIFacade;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.smartcardio.Card;
+import javax.smartcardio.CardChannel;
+import javax.smartcardio.CardException;
+import javax.smartcardio.CommandAPDU;
+import javax.smartcardio.ResponseAPDU;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import at.gv.egiz.bku.smccstal.AbstractRequestHandler;
+import at.gv.egiz.smcc.SignatureCardException;
+import at.gv.egiz.stal.ErrorResponse;
+import at.gv.egiz.stal.STALRequest;
+import at.gv.egiz.stal.STALResponse;
+import at.gv.egiz.stal.ext.APDUScriptRequest;
+import at.gv.egiz.stal.ext.APDUScriptResponse;
+import at.gv.egiz.stal.ext.APDUScriptRequest.Command;
+import at.gv.egiz.stal.ext.APDUScriptRequest.RequestScriptElement;
+import at.gv.egiz.stal.ext.APDUScriptRequest.Reset;
+import at.gv.egiz.stal.ext.APDUScriptResponse.Response;
+import at.gv.egiz.stal.ext.APDUScriptResponse.ATR;
+import at.gv.egiz.stal.ext.APDUScriptResponse.ResponseScriptElement;
+import java.awt.event.ActionListener;
+
+/**
+ * @author mcentner
+ *
+ */
+public class CardMgmtRequestHandler extends AbstractRequestHandler implements ActionListener {
+
+  /**
+   * Logging facility.
+   */
+  private static Log log = LogFactory.getLog(CardMgmtRequestHandler.class);
+  
+  /**
+   * The sequence counter.
+   */
+  private int sequenceNum = 0;
+
+  /**
+   * display script num
+   */
+  private int currentActivationScript = 0;
+
+  @Override
+  public STALResponse handleRequest(STALRequest request)
+      throws InterruptedException {
+
+    // APDU Script Request
+    if (request instanceof APDUScriptRequest) {
+
+      currentActivationScript++;
+      log.debug("handling APDU script " + currentActivationScript);
+      
+      Card icc = card.getCard();
+
+      if (icc == null) {
+        log.error("SignatureCard instance '" + card.getClass().getName() + "' does not support card management requests.");
+        return new ErrorResponse(1000);
+      }
+
+      List<RequestScriptElement> script = ((APDUScriptRequest) request).getScript();
+      ArrayList<ResponseScriptElement> responses = new ArrayList<ResponseScriptElement>(script.size());
+
+      ((ActivationGUIFacade) gui).showActivationProgressDialog(currentActivationScript, script.size(), this, "cancel");
+
+      try {
+        log.trace("begin exclusive");
+        icc.beginExclusive();
+
+        for (RequestScriptElement scriptElement : script) {
+          ((ActivationGUIFacade) gui).incrementProgress();
+          
+          if (scriptElement instanceof Command) {
+            log.trace("handling APDU script element COMMAND");
+            Command command = (Command) scriptElement;
+            CommandAPDU commandAPDU = new CommandAPDU(command.getCommandAPDU());
+
+            log.trace("get basicchannel");
+            CardChannel channel = icc.getBasicChannel();
+            
+            sequenceNum = command.getSequence();
+            log.debug("Transmit APDU (sequence=" + sequenceNum + ")");
+            log.trace(commandAPDU.toString());
+            ResponseAPDU responseAPDU = channel.transmit(commandAPDU);
+            log.trace(responseAPDU.toString());
+            
+            byte[] sw = new byte[] { 
+                (byte) (0xFF & responseAPDU.getSW1()),
+                (byte) (0xFF & responseAPDU.getSW2()) }; 
+            
+            responses.add(new Response(sequenceNum, responseAPDU.getData(), sw, 0));
+            
+            if (command.getExpectedSW() != null && 
+              !Arrays.equals(sw, command.getExpectedSW())) {
+              // unexpected SW
+              log.warn("Got unexpected SW. APDU-script execution stopped.");
+              break;
+            }
+            
+          } else if (scriptElement instanceof Reset) {
+
+            log.trace("handling APDU script element RESET");
+            sequenceNum = 0;
+            card.reset();
+            javax.smartcardio.ATR atr = icc.getATR();
+            log.trace("got ATR: " + atr.toString());
+            responses.add(new ATR(atr.getBytes()));
+
+            log.trace("regain exclusive access to card");
+            icc = card.getCard();
+            icc.beginExclusive();
+          }
+          
+        }
+
+      } catch (CardException e) {
+        log.info("Failed to execute APDU script.", e);
+        responses.add(new Response(sequenceNum, null, null, Response.RC_UNSPECIFIED));
+      } catch (SignatureCardException e) {
+        log.info("Failed to reset smart card.", e);
+        responses.add(new Response(sequenceNum, null, null, Response.RC_UNSPECIFIED));
+      } catch (RuntimeException e) {
+        log.error(e);
+        throw e;
+      } finally {
+        try {
+          icc.endExclusive();
+        } catch (CardException e) {
+          log.info(e);
+        }
+      }
+
+      log.trace("done handling APDU script " + currentActivationScript + ", return response containing " + responses.size() + " elements");
+      ((ActivationGUIFacade) gui).showIdleDialog(this, "cancel");
+      return new APDUScriptResponse(responses);
+      
+    } else {
+      log.error("Got unexpected STAL request: " + request);
+      return new ErrorResponse(1000);
+    }
+    
+  }
+
+  @Override
+  public boolean requireCard() {
+    return true;
+  }
+
+}
diff --git a/smccSTALExt/src/main/java/at/gv/egiz/bku/smccstal/GetPINStatusException.java b/smccSTALExt/src/main/java/at/gv/egiz/bku/smccstal/GetPINStatusException.java
new file mode 100644
index 00000000..66b15887
--- /dev/null
+++ b/smccSTALExt/src/main/java/at/gv/egiz/bku/smccstal/GetPINStatusException.java
@@ -0,0 +1,41 @@
+/*
+ * 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.bku.smccstal;
+
+import at.gv.egiz.smcc.SignatureCardException;
+
+/**
+ *
+ * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at>
+ */
+public class GetPINStatusException extends SignatureCardException {
+
+    /**
+     * Creates a new instance of <code>GetStatusException</code> without detail message.
+     */
+    public GetPINStatusException() {
+    }
+
+
+    /**
+     * Constructs an instance of <code>GetStatusException</code> with the specified detail message.
+     * @param msg the detail message.
+     */
+    public GetPINStatusException(String msg) {
+        super(msg);
+    }
+}
diff --git a/smccSTALExt/src/main/java/at/gv/egiz/bku/smccstal/ManagementPINProviderFactory.java b/smccSTALExt/src/main/java/at/gv/egiz/bku/smccstal/ManagementPINProviderFactory.java
new file mode 100644
index 00000000..34bcbf5c
--- /dev/null
+++ b/smccSTALExt/src/main/java/at/gv/egiz/bku/smccstal/ManagementPINProviderFactory.java
@@ -0,0 +1,262 @@
+/*
+ * 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.bku.smccstal;
+
+import at.gv.egiz.bku.gui.BKUGUIFacade;
+import at.gv.egiz.smcc.ChangePINProvider;
+import at.gv.egiz.bku.gui.PINManagementGUIFacade;
+import at.gv.egiz.bku.smccstal.AbstractPINProvider;
+import at.gv.egiz.bku.smccstal.PINProviderFactory;
+import at.gv.egiz.smcc.CancelledException;
+import at.gv.egiz.smcc.ccid.CCID;
+import at.gv.egiz.smcc.PINProvider;
+import at.gv.egiz.smcc.PINSpec;
+import at.gv.egiz.smcc.SignatureCard;
+
+/**
+ *
+ * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at>
+ */
+public class ManagementPINProviderFactory extends PINProviderFactory {
+
+  public ManagementPINProviderFactory(CCID reader, PINManagementGUIFacade gui) {
+    super(reader, gui);
+  }
+  
+//  public static ManagementPINProviderFactory getInstance(SignatureCard forCard,
+//          PINManagementGUIFacade gui) {
+//    if (forCard.getReader().hasFeature(CCID.FEATURE_VERIFY_PIN_DIRECT)) {
+//      return new PinpadPINProviderFactory(gui);
+//
+//    } else {
+//      return new SoftwarePINProviderFactory(gui);
+//    }
+//  }
+
+  public PINProvider getVerifyPINProvider() {
+    if (reader.hasFeature(CCID.FEATURE_VERIFY_PIN_START)) {
+      return new PinpadGenericPinProvider(PINManagementGUIFacade.DIALOG.VERIFY);
+    } else if (reader.hasFeature(CCID.FEATURE_VERIFY_PIN_DIRECT)) {
+      return new PinpadGenericPinProvider(PINManagementGUIFacade.DIALOG.VERIFY);
+    } else {
+      return new SoftwareGenericPinProvider(PINManagementGUIFacade.DIALOG.VERIFY);
+    }
+  }
+
+  public PINProvider getActivatePINProvider() {
+    if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_START)) {
+      return new PinpadGenericPinProvider(PINManagementGUIFacade.DIALOG.ACTIVATE);
+    } else if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_DIRECT)) {
+      return new PinpadGenericPinProvider(PINManagementGUIFacade.DIALOG.ACTIVATE);
+    } else {
+      return new SoftwareGenericPinProvider(PINManagementGUIFacade.DIALOG.ACTIVATE);
+    }
+  }
+
+  public ChangePINProvider getChangePINProvider() {
+    if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_START)) {
+      return new PinpadGenericPinProvider(PINManagementGUIFacade.DIALOG.CHANGE);
+    } else if (reader.hasFeature(CCID.FEATURE_MODIFY_PIN_DIRECT)) {
+      return new PinpadGenericPinProvider(PINManagementGUIFacade.DIALOG.CHANGE);
+    } else {
+      return new ChangePinProvider();
+    }
+  }
+
+  public PINProvider getUnblockPINProvider() {
+    if (reader.hasFeature(CCID.FEATURE_VERIFY_PIN_START)) {
+      return new PinpadGenericPinProvider(PINManagementGUIFacade.DIALOG.UNBLOCK);
+    } else if (reader.hasFeature(CCID.FEATURE_VERIFY_PIN_DIRECT)) {
+      return new PinpadGenericPinProvider(PINManagementGUIFacade.DIALOG.UNBLOCK);
+    } else {
+      return new SoftwareGenericPinProvider(PINManagementGUIFacade.DIALOG.UNBLOCK);
+    }
+  }
+
+  class PinpadGenericPinProvider extends AbstractPINProvider
+          implements ChangePINProvider {
+
+    protected PINManagementGUIFacade.DIALOG type;
+
+    private PinpadGenericPinProvider(PINManagementGUIFacade.DIALOG type) {
+      this.type = type;
+    }
+
+    @Override
+    public char[] providePIN(PINSpec spec, int retries)
+            throws CancelledException, InterruptedException {
+
+      showPinpadPINDialog(retries, spec);
+      retry = true;
+      return null;
+    }
+
+    /**
+     * do not call this method without calling providePIN()
+     * (no message is displayed)
+     * @param spec
+     * @param retries
+     * @return
+     */
+    @Override
+    public char[] provideOldPIN(PINSpec spec, int retries) {
+      return null;
+    }
+
+    private void showPinpadPINDialog(int retries, PINSpec pinSpec) {
+      String title, message;
+      Object[] params;
+      if (retry) {
+        if (retries == 1) {
+          message = BKUGUIFacade.MESSAGE_LAST_RETRY_PINPAD;
+        } else {
+          message = BKUGUIFacade.MESSAGE_RETRIES_PINPAD;
+        }
+        title = BKUGUIFacade.TITLE_RETRY;
+        params = new Object[]{String.valueOf(retries)};
+      } else if (type == PINManagementGUIFacade.DIALOG.VERIFY) {
+        title = PINManagementGUIFacade.TITLE_VERIFY_PIN;
+        message = BKUGUIFacade.MESSAGE_ENTERPIN_PINPAD;
+        String pinSize = String.valueOf(pinSpec.getMinLength());
+        if (pinSpec.getMinLength() != pinSpec.getMaxLength()) {
+          pinSize += "-" + pinSpec.getMaxLength();
+        }
+        params = new Object[]{pinSpec.getLocalizedName(), pinSize};
+      } else if (type == PINManagementGUIFacade.DIALOG.ACTIVATE) {
+        title = PINManagementGUIFacade.TITLE_ACTIVATE_PIN;
+        message = PINManagementGUIFacade.MESSAGE_ACTIVATEPIN_PINPAD;
+        String pinSize = String.valueOf(pinSpec.getMinLength());
+        if (pinSpec.getMinLength() != pinSpec.getMaxLength()) {
+          pinSize += "-" + pinSpec.getMaxLength();
+        }
+        params = new Object[]{pinSpec.getLocalizedName(), pinSize};
+      } else if (type == PINManagementGUIFacade.DIALOG.CHANGE) {
+        title = PINManagementGUIFacade.TITLE_CHANGE_PIN;
+        message = PINManagementGUIFacade.MESSAGE_CHANGEPIN_PINPAD;
+        String pinSize = String.valueOf(pinSpec.getMinLength());
+        if (pinSpec.getMinLength() != pinSpec.getMaxLength()) {
+          pinSize += "-" + pinSpec.getMaxLength();
+        }
+        params = new Object[]{pinSpec.getLocalizedName(), pinSize};
+      } else { //if (type == DIALOG.UNBLOCK) {
+        title = PINManagementGUIFacade.TITLE_UNBLOCK_PIN;
+        message = PINManagementGUIFacade.MESSAGE_UNBLOCKPIN_PINPAD;
+        String pinSize = String.valueOf(pinSpec.getMinLength());
+        if (pinSpec.getMinLength() != pinSpec.getMaxLength()) {
+          pinSize += "-" + pinSpec.getMaxLength();
+        }
+        params = new Object[]{pinSpec.getLocalizedName(), pinSize};
+      }
+      gui.showMessageDialog(title, message, params);
+    }
+  }
+
+
+  class SoftwareGenericPinProvider extends AbstractPINProvider {
+
+//    protected PINManagementGUIFacade gui;
+    protected PINManagementGUIFacade.DIALOG type;
+
+    private SoftwareGenericPinProvider(PINManagementGUIFacade.DIALOG type) {
+      this.type = type;
+    }
+
+    @Override
+    public char[] providePIN(PINSpec spec, int retries)
+            throws CancelledException, InterruptedException {
+
+      ((PINManagementGUIFacade) gui).showPINDialog(type, spec,
+              (retry) ? retries : -1,
+              this, "exec",
+              this, "back");
+
+      waitForAction();
+
+      if ("exec".equals(action)) {
+        gui.showMessageDialog(BKUGUIFacade.TITLE_WAIT,
+                BKUGUIFacade.MESSAGE_WAIT);
+        retry = true;
+        return gui.getPin();
+      } else if ("back".equals(action)) {
+        throw new CancelledException();
+      } else {
+        log.error("unsupported command " + action);
+        throw new CancelledException();
+      }
+    }
+  }
+
+  class ChangePinProvider extends AbstractPINProvider
+          implements ChangePINProvider {
+
+//    protected PINManagementGUIFacade gui;
+
+    private char[] oldPin;
+    private char[] newPin;
+
+    private ChangePinProvider() {
+    }
+
+    @Override
+    public char[] providePIN(PINSpec spec, int retries)
+            throws CancelledException, InterruptedException {
+      if (newPin == null) {
+        getPINs(spec, retries);
+      }
+      char[] pin = newPin;
+      newPin = null;
+      return pin;
+    }
+
+    @Override
+    public char[] provideOldPIN(PINSpec spec, int retries)
+            throws CancelledException, InterruptedException {
+      if (oldPin == null) {
+        getPINs(spec, retries);
+      }
+      char[] pin = oldPin;
+      oldPin = null;
+      return pin;
+    }
+
+    private void getPINs(PINSpec spec, int retries)
+            throws InterruptedException, CancelledException {
+
+      ((PINManagementGUIFacade) gui).showPINDialog(
+              PINManagementGUIFacade.DIALOG.CHANGE, spec,
+              (retry) ? retries : -1,
+              this, "exec",
+              this, "back");
+
+      waitForAction();
+
+      if ("exec".equals(action)) {
+        gui.showMessageDialog(BKUGUIFacade.TITLE_WAIT,
+                BKUGUIFacade.MESSAGE_WAIT);
+        retry = true;
+        oldPin = ((PINManagementGUIFacade) gui).getOldPin();
+        newPin = gui.getPin();
+      } else if ("back".equals(action)) {
+        throw new CancelledException();
+      } else {
+        log.error("unsupported command " + action);
+        throw new CancelledException();
+      }
+    }
+  }
+}
diff --git a/smccSTALExt/src/main/java/at/gv/egiz/bku/smccstal/PINManagementRequestHandler.java b/smccSTALExt/src/main/java/at/gv/egiz/bku/smccstal/PINManagementRequestHandler.java
new file mode 100644
index 00000000..bfeb90b0
--- /dev/null
+++ b/smccSTALExt/src/main/java/at/gv/egiz/bku/smccstal/PINManagementRequestHandler.java
@@ -0,0 +1,245 @@
+/*
+ * 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.bku.smccstal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import at.gv.egiz.bku.gui.BKUGUIFacade;
+import at.gv.egiz.bku.gui.PINManagementGUIFacade;
+import at.gv.egiz.bku.gui.PINManagementGUIFacade.STATUS;
+import at.gv.egiz.bku.smccstal.AbstractRequestHandler;
+import at.gv.egiz.smcc.CancelledException;
+import at.gv.egiz.smcc.LockedException;
+import at.gv.egiz.smcc.NotActivatedException;
+import at.gv.egiz.smcc.PINConfirmationException;
+import at.gv.egiz.smcc.PINFormatException;
+import at.gv.egiz.smcc.PINMgmtSignatureCard;
+import at.gv.egiz.smcc.PINOperationAbortedException;
+import at.gv.egiz.smcc.PINSpec;
+import at.gv.egiz.smcc.SignatureCardException;
+import at.gv.egiz.smcc.TimeoutException;
+import at.gv.egiz.smcc.PINMgmtSignatureCard.PIN_STATE;
+import at.gv.egiz.stal.ErrorResponse;
+import at.gv.egiz.stal.STALRequest;
+import at.gv.egiz.stal.STALResponse;
+import at.gv.egiz.stal.ext.PINManagementRequest;
+import at.gv.egiz.stal.ext.PINManagementResponse;
+
+/**
+ *
+ * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at>
+ */
+public class PINManagementRequestHandler extends AbstractRequestHandler {
+
+  protected static final Log log = LogFactory.getLog(PINManagementRequestHandler.class);
+
+  protected Map<PINSpec, STATUS> pinStates = new HashMap<PINSpec, STATUS>();
+
+  @Override
+  public STALResponse handleRequest(STALRequest request) throws InterruptedException {
+    if (request instanceof PINManagementRequest) {
+
+    PINManagementGUIFacade gui = (PINManagementGUIFacade) this.gui;
+      
+    PINSpec selectedPIN = null;
+
+    try {
+
+      if (card instanceof PINMgmtSignatureCard) {
+
+        // update all PIN states
+        for (PINSpec pinSpec : ((PINMgmtSignatureCard) card).getPINSpecs()) {
+          updatePINState(pinSpec, STATUS.UNKNOWN);
+        }
+        
+        gui.showPINManagementDialog(pinStates, this, "activate_enterpin",
+              "change_enterpin", "unblock_enterpuk", "verify_enterpin", this,
+              "cancel");
+        
+      } else {
+        
+        // card does not support PIN management
+        gui.showErrorDialog(PINManagementGUIFacade.ERR_UNSUPPORTED_CARD,
+              null, this, "cancel");
+        
+      }
+
+      while (true) {
+
+        waitForAction();
+
+        if ("cancel".equals(actionCommand)) {
+          log.debug("pin management cancel");
+          return new PINManagementResponse();
+        } else {
+          selectedPIN = gui.getSelectedPINSpec();
+
+          if (selectedPIN == null) {
+            throw new NullPointerException("no PIN selected for activation/change");
+          }
+
+          ManagementPINProviderFactory ppfac =
+                  new ManagementPINProviderFactory(card.getReader(), gui);
+
+          try {
+            if ("activate_enterpin".equals(actionCommand)) {
+              log.info("activate " + selectedPIN.getLocalizedName());
+              ((PINMgmtSignatureCard) card).activatePIN(selectedPIN,
+                      ppfac.getActivatePINProvider());
+              updatePINState(selectedPIN, STATUS.ACTIV);
+              gui.showMessageDialog(PINManagementGUIFacade.TITLE_ACTIVATE_SUCCESS,
+                      PINManagementGUIFacade.MESSAGE_ACTIVATE_SUCCESS,
+                      new Object[] {selectedPIN.getLocalizedName()},
+                      BKUGUIFacade.BUTTON_OK, this, "ok");
+              waitForAction();
+            } else if ("change_enterpin".equals(actionCommand)) {
+              log.info("change " + selectedPIN.getLocalizedName());
+              ((PINMgmtSignatureCard) card).changePIN(selectedPIN, 
+                      ppfac.getChangePINProvider());
+              updatePINState(selectedPIN, STATUS.ACTIV);
+              gui.showMessageDialog(PINManagementGUIFacade.TITLE_CHANGE_SUCCESS,
+                      PINManagementGUIFacade.MESSAGE_CHANGE_SUCCESS,
+                      new Object[] {selectedPIN.getLocalizedName()},
+                      BKUGUIFacade.BUTTON_OK, this, "ok");
+              waitForAction();
+
+            } else if ("unblock_enterpuk".equals(actionCommand)) {
+              log.info("unblock " + selectedPIN.getLocalizedName());
+              ((PINMgmtSignatureCard) card).unblockPIN(selectedPIN,
+                      ppfac.getUnblockPINProvider());
+            } else if ("verify_enterpin".equals(actionCommand)) {
+              log.info("verify " + selectedPIN.getLocalizedName());
+              ((PINMgmtSignatureCard) card).verifyPIN(selectedPIN,
+                      ppfac.getVerifyPINProvider());
+              updatePINState(selectedPIN, STATUS.ACTIV);
+            }
+          } catch (CancelledException ex) {
+            log.trace("cancelled");
+          } catch (TimeoutException ex) {
+            log.error("Timeout during pin entry");
+            gui.showMessageDialog(BKUGUIFacade.TITLE_ENTRY_TIMEOUT,
+                    BKUGUIFacade.ERR_PIN_TIMEOUT, 
+                    new Object[] {selectedPIN.getLocalizedName()},
+                    BKUGUIFacade.BUTTON_OK, this, null);
+            waitForAction();
+          } catch (LockedException ex) {
+            log.error(selectedPIN.getLocalizedName() + " locked");
+            updatePINState(selectedPIN, STATUS.BLOCKED);
+            gui.showErrorDialog(PINManagementGUIFacade.ERR_LOCKED,
+                    new Object[] {selectedPIN.getLocalizedName()},
+                    this, null);
+            waitForAction();
+          } catch (NotActivatedException ex) {
+            log.error(selectedPIN.getLocalizedName() + " not active");
+            updatePINState(selectedPIN, STATUS.NOT_ACTIV);
+            gui.showErrorDialog(PINManagementGUIFacade.ERR_NOT_ACTIVE,
+                    new Object[] {selectedPIN.getLocalizedName()},
+                    this, null);
+            waitForAction();
+          } catch (PINConfirmationException ex) {
+            log.error("confirmation pin does not match new " + selectedPIN.getLocalizedName());
+            gui.showErrorDialog(PINManagementGUIFacade.ERR_PIN_CONFIRMATION,
+                    new Object[] {selectedPIN.getLocalizedName()},
+                    this, null);
+            waitForAction();
+          } catch (PINOperationAbortedException ex) {
+            log.error("pin operation aborted without further details");
+            gui.showErrorDialog(PINManagementGUIFacade.ERR_PIN_OPERATION_ABORTED,
+                    new Object[] {selectedPIN.getLocalizedName()},
+                    this, null);
+            waitForAction();
+          } catch (PINFormatException ex) {
+            log.error("wrong format of new " + selectedPIN.getLocalizedName());
+//            updatePINStatus(selectedPIN, STATUS.NOT_ACTIV);
+            String pinSize = String.valueOf(selectedPIN.getMinLength());
+            if (selectedPIN.getMinLength() != selectedPIN.getMaxLength()) {
+                pinSize += "-" + selectedPIN.getMaxLength();
+            }
+            gui.showErrorDialog(PINManagementGUIFacade.ERR_PIN_FORMAT,
+                    new Object[] {selectedPIN.getLocalizedName(), pinSize},
+                    this, null);
+            waitForAction();
+          }
+        } // end if
+
+        selectedPIN = null;
+        gui.showPINManagementDialog(pinStates,
+                this, "activate_enterpin", "change_enterpin", "unblock_enterpuk", "verify_enterpin",
+                this, "cancel");
+      } // end while
+
+      } catch (GetPINStatusException ex) {
+        String pin = (selectedPIN != null) ? selectedPIN.getLocalizedName() : "pin";
+        log.error("failed to get " +  pin + " status: " + ex.getMessage());
+        gui.showErrorDialog(PINManagementGUIFacade.ERR_STATUS, null,
+                this, "ok");
+        waitForAction();
+        return new ErrorResponse(1000);
+      } catch (SignatureCardException ex) {
+        log.error(ex.getMessage(), ex);
+        gui.showErrorDialog(PINManagementGUIFacade.ERR_UNKNOWN, null,
+                this, "ok");
+        waitForAction();
+        return new ErrorResponse(1000);
+      }
+    } else {
+      log.error("Got unexpected STAL request: " + request);
+      return new ErrorResponse(1000);
+    }
+  }
+
+  @Override
+  public boolean requireCard() {
+    return true;
+  }
+
+  /**
+   * query status for STARCOS card,
+   * assume provided status for ACOS card
+   * @param pinSpec
+   * @param status
+   * @throws at.gv.egiz.smcc.SignatureCardException if query status fails
+   */
+  private void updatePINState(PINSpec pinSpec, STATUS status)
+      throws GetPINStatusException {
+
+    PINMgmtSignatureCard pmCard = ((PINMgmtSignatureCard) card);
+    PIN_STATE pinState;
+    try {
+      pinState = pmCard.getPINState(pinSpec);
+    } catch (SignatureCardException e) {
+      String msg = "Failed to get PIN status for pin '"
+          + pinSpec.getLocalizedName() + "'.";
+      log.info(msg, e);
+      throw new GetPINStatusException(msg);
+    }
+    if (pinState == PIN_STATE.ACTIV) {
+      pinStates.put(pinSpec, STATUS.ACTIV);
+    } else if (pinState == PIN_STATE.NOT_ACTIV) {
+      pinStates.put(pinSpec, STATUS.NOT_ACTIV);
+    } else if (pinState == PIN_STATE.BLOCKED) {
+      pinStates.put(pinSpec, STATUS.BLOCKED);
+    } else {
+      pinStates.put(pinSpec, status);
+    }
+  }
+
+}
-- 
cgit v1.2.3