From b9ccb62d35a755efb505d426ce924d5a8fbe937a Mon Sep 17 00:00:00 2001
From: "Bonato, Martin" <martin.bonato@prime-sign.com>
Date: Thu, 8 Feb 2018 22:19:55 +0100
Subject: BulkSignature implementation

---
 .../main/java/at/gv/egiz/bku/gui/BKUGUIFacade.java |  21 +-
 .../main/java/at/gv/egiz/bku/gui/BKUGUIImpl.java   | 257 ++++++++++++++++++---
 .../egiz/bku/gui/hashdata/HashDataInputLoader.java |  16 ++
 .../at/gv/egiz/bku/gui/viewer/SecureViewer.java    |  58 +++++
 .../gv/egiz/stal/impl/ByteArrayHashDataInput.java  |  20 +-
 .../at/gv/egiz/bku/gui/Messages.properties         |   6 +-
 .../at/gv/egiz/bku/gui/Messages_de.properties      |   6 +-
 .../test/java/at/gv/egiz/bku/gui/BKUGUIWorker.java |   2 +-
 8 files changed, 354 insertions(+), 32 deletions(-)
 create mode 100644 BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/hashdata/HashDataInputLoader.java
 create mode 100644 BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/viewer/SecureViewer.java

(limited to 'BKUCommonGUI/src')

diff --git a/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/BKUGUIFacade.java b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/BKUGUIFacade.java
index 5de6a9e0..c128ceab 100644
--- a/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/BKUGUIFacade.java
+++ b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/BKUGUIFacade.java
@@ -24,8 +24,10 @@
 
 package at.gv.egiz.bku.gui;
 
-  import at.gv.egiz.stal.HashDataInput;
+import at.gv.egiz.bku.gui.hashdata.HashDataInputLoader;
 import at.gv.egiz.smcc.PinInfo;
+  import at.gv.egiz.stal.HashDataInput;
+
 import java.awt.Color;
 import java.awt.event.ActionListener;
 import java.util.List;
@@ -67,6 +69,7 @@ public interface BKUGUIFacade {
   public static final String TITLE_INSERTCARD = "title.insertcard";
   public static final String TITLE_CARD_NOT_SUPPORTED = "title.cardnotsupported";
   public static final String TITLE_VERIFY_PIN = "title.verify.pin";
+  public static final String TITLE_OVERRULE_PINPAD = "title.overrule.pinpad";
   public static final String TITLE_SIGN = "title.sign";
   public static final String TITLE_VERIFY_PINPAD = "title.verify.pinpad";
   public static final String TITLE_ERROR = "title.error";
@@ -74,6 +77,7 @@ public interface BKUGUIFacade {
   public static final String TITLE_ENTRY_TIMEOUT = "title.entry.timeout";
   public static final String TITLE_RETRY = "title.retry";
   public static final String TITLE_WAIT = "title.wait";
+  public static final String TITLE_BULKSIGNATURE = "title.bulksign";
   public static final String TITLE_SIGNATURE_DATA = "title.signature.data";
   public static final String WINDOWTITLE_SAVE = "windowtitle.save";
   public static final String WINDOWTITLE_ERROR = "windowtitle.error";
@@ -89,6 +93,7 @@ public interface BKUGUIFacade {
   public static final String MESSAGE_CARD_NOT_SUPPORTED = "cardnotsupported";
   public static final String MESSAGE_ENTERPIN = "enterpin";
   public static final String MESSAGE_ENTERPIN_PINPAD = "enterpin.pinpad";
+  public static final String MESSAGE_OVERRULE_PINPAD = "overrule.pinpad";
   public static final String MESSAGE_ENTERPIN_PINPAD_DIRECT = "enterpin.pinpad.direct";
   public static final String MESSAGE_HASHDATALINK = "hashdatalink";
   public static final String MESSAGE_HASHDATALINK_TINY = "hashdatalink.tiny";
@@ -103,6 +108,7 @@ public interface BKUGUIFacade {
   public static final String MESSAGE_LAST_RETRY_PINPAD = "retries.pinpad.last";
   public static final String MESSAGE_OVERWRITE = "overwrite";
   public static final String MESSAGE_HELP = "help";
+  public static final String MESSAGE_BULKSIGN = "bulksign";
 
   public static final String WARNING_XHTML = "warning.xhtml";
   public static final String WARNING_CERT_NOTYETVALID = "warning.cert.notyetvalid";
@@ -165,6 +171,11 @@ public interface BKUGUIFacade {
           ActionListener cancelListener, String cancelCommand,
           ActionListener viewerListener, String viewerCommand);
 
+  public void showSignaturePINDialog(PinInfo pinSpec, int numRetries, int numSignatures, 
+          ActionListener signListener, String signCommand,
+          ActionListener cancelListener, String cancelCommand,
+          ActionListener viewerListener, String viewerCommand);
+
   public char[] getPin();
 
   /**
@@ -176,7 +187,7 @@ public interface BKUGUIFacade {
    * @param backCommand
    */
   public void showSecureViewer(List<HashDataInput> dataToBeSigned,
-          ActionListener backListener, String backCommand);
+      ActionListener backListener, String backCommand, HashDataInputLoader hashDataInputLoader);
 
   public void showErrorDialog(String errorMsgKey, Object[] errorMsgParams,
           ActionListener okListener, String okCommand);
@@ -196,8 +207,14 @@ public interface BKUGUIFacade {
   public void showMessageDialog(String titleKey,
           String msgKey, Object[] msgParams);
 
+	void updateMessageDialog(String titleKey, 
+			String msgKey, Object[] msgParams, String buttonKey, ActionListener okListener, String okCommand);
+
   public void showMessageDialog(String titleKey,
           String msgKey);
   
   public void getFocusFromBrowser();
+
+  public void showPinPadDeactivationDialog(ActionListener okListener, String okCommand, 
+      ActionListener cancelListener, String cancelCommand);
 }
diff --git a/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/BKUGUIImpl.java b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/BKUGUIImpl.java
index 82513a0e..13e330bf 100644
--- a/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/BKUGUIImpl.java
+++ b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/BKUGUIImpl.java
@@ -25,6 +25,7 @@
 
 package at.gv.egiz.bku.gui;
 
+import at.gv.egiz.bku.gui.hashdata.HashDataInputLoader;
 import at.gv.egiz.bku.gui.viewer.FontProviderException;
 import at.gv.egiz.bku.gui.viewer.FontProvider;
 import at.gv.egiz.bku.gui.viewer.SecureViewerSaveDialog;
@@ -67,6 +68,7 @@ import javax.swing.JPasswordField;
 import javax.swing.JScrollPane;
 import javax.swing.JTable;
 import javax.swing.LayoutStyle;
+import javax.swing.LayoutStyle.ComponentPlacement;
 import javax.swing.ListSelectionModel;
 import javax.swing.SwingUtilities;
 import javax.swing.UIManager;
@@ -90,7 +92,7 @@ public class BKUGUIImpl implements BKUGUIFacade {
 	}
 
 	protected Component primaryFocusHolder;
-	protected SecureViewerDialog secureViewer;
+	protected SecureViewerDialog secureViewerDialog;
 
 	protected HelpListener helpListener;
 	protected FontProvider fontProvider;
@@ -155,7 +157,9 @@ public class BKUGUIImpl implements BKUGUIFacade {
 //	protected HashDataInput storedSelection;
 	protected List<HashDataInput> signedReferences;
 	protected Integer referenceIndex;
-	private at.gv.egiz.bku.gui.BKUGUIImpl.SignedReferencesSelectionListener.SignedReferencesListDisplayer storedBackToListListener;
+	protected at.gv.egiz.bku.gui.BKUGUIImpl.SignedReferencesSelectionListener.SignedReferencesListDisplayer storedBackToListListener;
+	
+	protected HashDataInputLoader hashDataInputLoader;
 
 	/**
 	 * set contentPane init message bundle configure the style register the help
@@ -1205,6 +1209,15 @@ public class BKUGUIImpl implements BKUGUIFacade {
             final ActionListener signListener, final String signCommand,
             final ActionListener cancelListener, final String cancelCommand,
             final ActionListener hashdataListener, final String hashdataCommand) {
+    showSignaturePINDialog(pinSpec, numRetries, 1, signListener, signCommand, cancelListener, cancelCommand,
+        hashdataListener, hashdataCommand);
+  }
+
+  @Override
+  public void showSignaturePINDialog(final PinInfo pinSpec, final int numRetries, final int numSignatures,
+            final ActionListener signListener, final String signCommand,
+            final ActionListener cancelListener, final String cancelCommand,
+            final ActionListener hashdataListener, final String hashdataCommand) {
 
 		log.debug("Scheduling signature-pin dialog.");
 
@@ -1228,7 +1241,9 @@ public class BKUGUIImpl implements BKUGUIFacade {
 
 				if (renderHeaderPanel) {
 					if (numRetries < 0) {
-						titleLabel.setText(getMessage(TITLE_SIGN));
+					  String msgPattern = getMessage(TITLE_SIGN);
+					  String msg = MessageFormat.format(msgPattern, numSignatures);
+						titleLabel.setText(msg);
 					} else {
 						titleLabel.setText(getMessage(TITLE_RETRY));
 					}
@@ -1571,6 +1586,35 @@ public class BKUGUIImpl implements BKUGUIFacade {
 		showMessageDialog(titleKey, null, msgKey, msgParams, null, null, null);
 	}
 
+	
+	 @Override
+	  public void updateMessageDialog(final String titleKey, final String msgKey,
+	      final Object[] msgParams, String buttonKey,
+				final ActionListener okListener, final String okCommand) {
+
+     SwingUtilities.invokeLater(new Runnable() {
+
+       @Override
+       public void run() {
+
+         log.debug("[{}] Update message dialog.", Thread.currentThread().getName());
+
+
+         if (renderHeaderPanel) {
+           titleLabel.setText(getMessage(titleKey));
+         }
+
+         helpListener.setHelpTopic(msgKey);
+
+         String msgPattern = getMessage(msgKey);
+         String msg = MessageFormat.format(msgPattern, msgParams);
+
+         msgLabel.setText(msg);
+
+       }
+     });
+	  }
+
 	@Override
 	public void showMessageDialog(final String titleKey, final String msgKey) {
 
@@ -1718,6 +1762,158 @@ public class BKUGUIImpl implements BKUGUIFacade {
 		});
 	}
 
+
+  private void showOptionDialog(final String titleKey, final String msgKey, final Object[] msgParams,
+      final String cancelButtonKey, final String okButtonKey, final ActionListener cancelListener,
+      final String cancelCommand, final ActionListener okListener, final String okCommand) {
+
+    log.debug("Scheduling message dialog.");
+
+    SwingUtilities.invokeLater(new Runnable() {
+
+      @Override
+      public void run() {
+
+        log.debug("[{}] Show option dialog.", Thread.currentThread().getName());
+
+        log.debug("ButtonKey: {}.", okButtonKey);
+        
+        log.debug("ButtonKey: {}.", cancelButtonKey);
+
+        mainPanel.removeAll();
+        buttonPanel.removeAll();
+
+        if (renderHeaderPanel) {
+          titleLabel.setText(getMessage(titleKey));
+        }
+
+        helpListener.setHelpTopic(msgKey);
+
+        String msgPattern = getMessage(msgKey);
+        String msg = MessageFormat.format(msgPattern, msgParams);
+
+        // we need to create a new JLabel object every time in order to
+        // ensure
+        // that screen reading software will read each updated label
+        msgLabel = new JLabel();
+
+        msgLabel.setFocusable(true);
+
+        msgLabel.setFont(msgLabel.getFont().deriveFont(
+            msgLabel.getFont().getStyle() & ~Font.BOLD));
+        msgLabel.setText(msg);
+
+        GroupLayout mainPanelLayout = new GroupLayout(mainPanel);
+        mainPanel.setLayout(mainPanelLayout);
+
+        GroupLayout.ParallelGroup mainHorizontal = mainPanelLayout
+            .createParallelGroup(GroupLayout.Alignment.LEADING);
+        GroupLayout.SequentialGroup mainVertical = mainPanelLayout
+            .createSequentialGroup();
+
+        String accessibleData = "";
+
+        if (!renderHeaderPanel) {
+          msgTitleLabel = new JLabel();
+          msgTitleLabel.setFont(msgTitleLabel.getFont().deriveFont(
+              msgTitleLabel.getFont().getStyle() | Font.BOLD));
+          msgTitleLabel.setText(getMessage(titleKey));
+
+          accessibleData = accessibleData + getMessage(titleKey);
+
+          GroupLayout.SequentialGroup titleHorizontal = mainPanelLayout
+              .createSequentialGroup()
+              .addComponent(msgTitleLabel);
+
+          GroupLayout.ParallelGroup titleVertical = mainPanelLayout
+              .createParallelGroup(GroupLayout.Alignment.LEADING)
+              .addComponent(msgTitleLabel);
+
+          if (helpListener.implementsListener()) {
+            titleHorizontal.addPreferredGap(
+                LayoutStyle.ComponentPlacement.UNRELATED, 0,
+                Short.MAX_VALUE).addComponent(helpLabel);
+            titleVertical.addComponent(helpLabel);
+          }
+
+          mainHorizontal.addGroup(titleHorizontal);
+          mainVertical.addGroup(titleVertical);
+
+        } else {
+
+          accessibleData = accessibleData + titleLabel.getText();
+        }
+
+        msgLabel.getAccessibleContext().setAccessibleName(
+            accessibleData + msgLabel.getText());
+        msgLabel.getAccessibleContext().setAccessibleDescription(
+            accessibleData + msgLabel.getText());
+
+        mainPanelLayout.setHorizontalGroup(mainHorizontal
+            .addComponent(msgLabel));
+        mainPanelLayout.setVerticalGroup(mainVertical
+            .addComponent(msgLabel));
+
+
+        cancelButton.setFont(cancelButton.getFont().deriveFont(cancelButton.getFont().getStyle() & ~java.awt.Font.BOLD));
+        cancelButton.setText(getMessage((cancelButtonKey != null) ? cancelButtonKey : BUTTON_CANCEL));
+        cancelButton.setActionCommand(okCommand);
+        cancelButton.addActionListener(okListener);
+
+        okButton.setFont(okButton.getFont().deriveFont(okButton.getFont().getStyle() & ~java.awt.Font.BOLD));
+        okButton.setText(getMessage((okButtonKey != null) ? okButtonKey : BUTTON_OK));
+        okButton.setActionCommand(cancelCommand);
+        okButton.addActionListener(okListener);
+          
+          primaryFocusHolder = okButton;
+     
+        renderShowOptionDialogueButtonPanel();
+        
+        GroupLayout buttonPanelLayout = new GroupLayout(buttonPanel);
+        buttonPanel.setLayout(buttonPanelLayout);
+
+        buttonPanelLayout.setHorizontalGroup(buttonPanelLayout
+            .createSequentialGroup().addComponent(okButton,
+                GroupLayout.PREFERRED_SIZE, buttonSize,
+                GroupLayout.PREFERRED_SIZE));
+        buttonPanelLayout.setVerticalGroup(buttonPanelLayout
+            .createSequentialGroup().addComponent(okButton));
+
+        // okListener might be null (up to windowCloseAdapter what to do)
+        if (windowCloseAdapter != null) {
+          windowCloseAdapter.registerListener(okListener, okCommand);
+        }
+
+        updateMethodToRunAtResize("at.gv.egiz.bku.gui.BKUGUIImpl",
+            "renderShowOptionDialogueButtonPanel");
+
+        // put focus to msgLabel to guarantee that label is read by
+        // screen reader upon loading
+        msgLabel.requestFocus();
+        msgLabel.setFocusable(false);
+
+        contentPanel.validate();
+
+        resize();
+      }
+    });
+  }
+
+
+  public void renderShowOptionDialogueButtonPanel() {
+
+    GroupLayout buttonPanelLayout = new GroupLayout(buttonPanel);
+    buttonPanel.setLayout(buttonPanelLayout);
+
+    buttonPanelLayout.setHorizontalGroup(buttonPanelLayout.createSequentialGroup()
+        .addComponent(cancelButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE)
+        .addComponent(okButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE)
+        .addPreferredGap(ComponentPlacement.UNRELATED));
+
+    buttonPanelLayout.setVerticalGroup(buttonPanelLayout.createParallelGroup().addComponent(cancelButton).addComponent(okButton));
+
+}
+  
 	public void renderShowMessageDialogueButtonPanel() {
 
 		if (showMessageOKButton) {
@@ -1757,7 +1953,9 @@ public class BKUGUIImpl implements BKUGUIFacade {
 	 */
 	@Override
 	public void showSecureViewer(final List<HashDataInput> dataToBeSigned,
-			final ActionListener backListener, final String backCommand) {
+      final ActionListener backListener, final String backCommand, HashDataInputLoader hashDataInputLoader) {
+
+    this.hashDataInputLoader = hashDataInputLoader;
 
 		if (dataToBeSigned == null) {
 			showErrorDialog(getMessage(ERR_NO_HASHDATA),
@@ -1819,7 +2017,7 @@ public class BKUGUIImpl implements BKUGUIFacade {
 			throws FontProviderException {
 
 		log.debug("[{}] Show secure viewer.", Thread.currentThread().getName());
-		secureViewer = new SecureViewerDialog(null, messages, closeListener,
+		secureViewerDialog = new SecureViewerDialog(null, messages, closeListener,
 				closeCommand, fontProvider, helpListener, getResizeFactor());
 
 		// workaround for [#439]
@@ -1829,56 +2027,55 @@ public class BKUGUIImpl implements BKUGUIFacade {
 		//Window window = SwingUtilities.getWindowAncestor(contentPane);
 		//if (window != null && window.isAlwaysOnTop()) {
 			log.debug("Make secureViewer alwaysOnTop.");
-			secureViewer.setAlwaysOnTop(true);
+			secureViewerDialog.setAlwaysOnTop(true);
 		//}
 
-		secureViewer.setContent(dataToBeSigned);
+		secureViewerDialog.setContent(dataToBeSigned);
 		log.trace("Viewer setContent returned.");
 	}
 
 	private void openSecureViewerDialog() {
 		
-		final HashDataInput storedSelection = signedReferences.get(referenceIndex);
+    try {
+      
+      log.trace("Opening SecureViewer dialog for list entry {}", referenceIndex);
+      final HashDataInput storedSelection = hashDataInputLoader.getHashDataInput(signedReferences.get(referenceIndex));
 		
-		if (SecureViewerDialog.SUPPORTED_MIME_TYPES.contains(storedSelection
-				.getMimeType())) {
+      if (SecureViewerDialog.SUPPORTED_MIME_TYPES.contains(storedSelection.getMimeType())) {
 			log.debug("[{}] Scheduling secure viewer dialog.", Thread.currentThread().getName());
 
-			showMessageDialog(TITLE_SIGNATURE_DATA,
-					MESSAGE_HASHDATA_VIEWER);
+        showMessageDialog(TITLE_SIGNATURE_DATA, MESSAGE_HASHDATA_VIEWER);
 
 			SwingUtilities.invokeLater(new Runnable() {
 
 				@Override
 				public void run() {
 					try {
-						showSecureViewer(storedSelection, storedBackToListListener,
-								null);
+              showSecureViewer(storedSelection, storedBackToListListener, null);
 						// SecureViewerDialog.showSecureViewer(selection,
 						// messages, fontProvider,
 						// helpMouseListener.getActionListener(),
 						// false);
 					} catch (FontProviderException ex) {
 						log.error("Failed to display secure viewer.", ex);
-						showErrorDialog(BKUGUIFacade.ERR_VIEWER,
-								new Object[] { ex.getMessage() },
-								storedBackToListListener, null);
+              showErrorDialog(BKUGUIFacade.ERR_VIEWER, new Object[] { ex.getMessage() }, storedBackToListListener, null);
 					}
 
 				}
 			});
 		} else {
-			log.debug("[{}] Mime-type not supported by secure viewer, " +
-					"scheduling save dialog.", Thread.currentThread().getName());
-			showMessageDialog(BKUGUIFacade.TITLE_SIGNATURE_DATA,
-					BKUGUIFacade.MESSAGE_UNSUPPORTED_MIMETYPE,
+        log.debug("[{}] Mime-type not supported by secure viewer, " + "scheduling save dialog.", Thread.currentThread()
+            .getName());
+        showMessageDialog(BKUGUIFacade.TITLE_SIGNATURE_DATA, BKUGUIFacade.MESSAGE_UNSUPPORTED_MIMETYPE,
 					new Object[] { storedSelection.getMimeType() });
-			SecureViewerSaveDialog.showSaveDialog(contentPane, storedSelection, messages,
-					storedBackToListListener, null,
+        SecureViewerSaveDialog.showSaveDialog(contentPane, storedSelection, messages, storedBackToListListener, null,
 					(int) (baseFontSize * getResizeFactor()));
 		}		
 		
-		
+    } catch (Exception ex) {
+      log.error("Failed to display secure viewer.", ex);
+      showErrorDialog(BKUGUIFacade.ERR_VIEWER, new Object[] { ex.getMessage() }, storedBackToListListener, null);
+    }
 	}
 	
 	private void showSignedReferencesListDialog(
@@ -2446,9 +2643,9 @@ public class BKUGUIImpl implements BKUGUIFacade {
 
 		}
 		
-		if (secureViewer != null && secureViewer.isVisible()) {
+		if (secureViewerDialog != null && secureViewerDialog.isVisible()) {
 
-			secureViewer.resize(factor);
+			secureViewerDialog.resize(factor);
 		}
 
 		try {
@@ -2682,4 +2879,12 @@ public class BKUGUIImpl implements BKUGUIFacade {
 			}
 		}
 	}
+
+  @Override
+  public void showPinPadDeactivationDialog(ActionListener okListener, String okCommand, ActionListener cancelListener,
+      String cancelCommand) {
+    
+    showOptionDialog(TITLE_OVERRULE_PINPAD, MESSAGE_OVERRULE_PINPAD, null, null, null, cancelListener, cancelCommand, okListener, okCommand);
+    
+  }
 }
diff --git a/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/hashdata/HashDataInputLoader.java b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/hashdata/HashDataInputLoader.java
new file mode 100644
index 00000000..1946dd2d
--- /dev/null
+++ b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/hashdata/HashDataInputLoader.java
@@ -0,0 +1,16 @@
+package at.gv.egiz.bku.gui.hashdata;
+
+import at.gv.egiz.stal.HashDataInput;
+
+public interface HashDataInputLoader {
+
+  
+  
+  /**
+   * Loads input data of referenced HashDataInput.
+   * @param hashDataInput HashDataInput without content that references a HashDataInput at server side with digest or referenceId.
+   * @return HashDataInput Referenced HashDataInput from server-side including content.
+   * @throws Exception
+   */
+  HashDataInput getHashDataInput(HashDataInput hashDataInput) throws Exception;
+}
diff --git a/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/viewer/SecureViewer.java b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/viewer/SecureViewer.java
new file mode 100644
index 00000000..02e0ceb8
--- /dev/null
+++ b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/viewer/SecureViewer.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2011 by Graz University of Technology, Austria
+ * MOCCA has been developed by the E-Government Innovation Center EGIZ, a joint
+ * initiative of the Federal Chancellery Austria and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ * http://www.osor.eu/eupl/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ */
+
+
+package at.gv.egiz.bku.gui.viewer;
+
+import at.gv.egiz.stal.SignatureInfo;
+
+import java.awt.event.ActionListener;
+import java.util.List;
+
+/**
+ *
+ * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at>
+ */
+public interface SecureViewer {
+
+  /**
+   * Displays the hashdata inputs for all provided dsig:SignedReferences.
+   * Implementations may verify the digest value if necessary.
+   * (LocalSignRequestHandler operates on DataObjectHashDataInput,
+   * other SignRequestHandlers should cache the HashDataInputs obtained by webservice calls,
+   * or simply forward to a HashDataInputServlet.)
+   * @param signedReferences The caller may select a subset of the references in SignedInfo to be displayed.
+   * @throws java.security.DigestException if digest values are verified and do not correspond
+   * (or any other digest computation error occurs)
+   * @throws java.lang.Exception
+   */
+  void displayDataToBeSigned(SignatureInfo signatureInfo,
+          ActionListener okListener, String okCommand)
+        throws Exception;
+  
+  
+  void displayDataToBeSigned(List<SignatureInfo> signatureInfo,
+      ActionListener okListener, String okCommand)
+    throws Exception;
+
+}
diff --git a/BKUCommonGUI/src/main/java/at/gv/egiz/stal/impl/ByteArrayHashDataInput.java b/BKUCommonGUI/src/main/java/at/gv/egiz/stal/impl/ByteArrayHashDataInput.java
index a28b3b56..ecf5f364 100644
--- a/BKUCommonGUI/src/main/java/at/gv/egiz/stal/impl/ByteArrayHashDataInput.java
+++ b/BKUCommonGUI/src/main/java/at/gv/egiz/stal/impl/ByteArrayHashDataInput.java
@@ -41,6 +41,7 @@ public class ByteArrayHashDataInput implements HashDataInput {
     private final Logger log = LoggerFactory.getLogger(ByteArrayHashDataInput.class);
   
     protected byte[] hashData;
+    protected byte[] digest;
     protected String id;
     protected String mimeType;
     protected String encoding;
@@ -57,6 +58,18 @@ public class ByteArrayHashDataInput implements HashDataInput {
         this.filename = filename;
     }
     
+    public ByteArrayHashDataInput(byte[] hashData, String id, String mimeType, String encoding, String filename, byte[] digest) {
+      if (hashData == null) {
+          throw new NullPointerException("HashDataInput not provided.");
+      }
+      this.hashData = hashData;
+      this.id = id;
+      this.mimeType = mimeType;
+      this.encoding = encoding;
+      this.filename = filename;
+      this.digest = digest;
+  }
+    
     /**
      * caches the hashdata input's stream
      * @param hdi to be cached
@@ -65,9 +78,9 @@ public class ByteArrayHashDataInput implements HashDataInput {
       if (hdi == null) {
         throw new NullPointerException("HashDataInput not provided.");
       }
+      try {
       InputStream is = hdi.getHashDataInput();
       ByteArrayOutputStream baos = new ByteArrayOutputStream();
-      try {
         byte[] buffer = new byte[1024];
         for (int i = is.read(buffer); i > -1; i = is.read(buffer)) {
           baos.write(buffer, 0, i);
@@ -111,5 +124,10 @@ public class ByteArrayHashDataInput implements HashDataInput {
     return filename;
   }
 
+  @Override
+  public byte[] getDigest() {
+    return digest;
+  }
+
     
 }
diff --git a/BKUCommonGUI/src/main/resources/at/gv/egiz/bku/gui/Messages.properties b/BKUCommonGUI/src/main/resources/at/gv/egiz/bku/gui/Messages.properties
index 434798ee..f7907561 100644
--- a/BKUCommonGUI/src/main/resources/at/gv/egiz/bku/gui/Messages.properties
+++ b/BKUCommonGUI/src/main/resources/at/gv/egiz/bku/gui/Messages.properties
@@ -23,8 +23,10 @@ title.welcome=<html>Welcome</html>
 title.insertcard=<html>No citizen card found</html>
 title.cardnotsupported=<html>This card is not supported</html>
 title.verify.pin=<html>Reading card</html>
-title.sign=<html>Create signature</html>
+title.sign=<html>Create {0,choice,1#signature|1<{0} signatures}</html>
+title.bulksign=<html>Create bulk signature</html>
 title.verify.pinpad=<html>Enter PIN and confirm</html>
+title.overrule.pinpad=<html>Allow keyboard input</html>
 title.error=<html>Error</html>
 title.warning=<html>Warning</html>
 title.entry.timeout=<html>Timeout</html>
@@ -41,11 +43,13 @@ windowtitle.help=Citizen card help
 # removed message.* prefix to reuse keys as help keys
 welcome=<html>Please wait...</html>
 wait=<html>Please wait...</html>
+bulksign=<html>Signing document {0} of {1}</html>
 insertcard=<html>Please insert your citizen card into the reader</html>
 cardnotsupported=<html>Please insert your citizen card into the reader</html>
 enterpin=<html>Enter {0}</html>
 enterpin.pinpad=<html>Enter PIN on card reader pinpad and confirm</html>
 enterpin.pinpad.direct=<html>Enter {0} ({1} digits) on card reader pinpad and confirm</html>
+overrule.pinpad=<html>Pin input on smart card reader is not supported for bulk signature requests.</html>
 hashdatalink=<html><a href=\"anzeige\">Display signature data</a></html>
 hashdatalink.tiny=<html><a href=\"anzeige\">signature data</a></html>
 hashdatalink.focus=<html><a href=\"anzeige\">[Display signature data]</a></html>
diff --git a/BKUCommonGUI/src/main/resources/at/gv/egiz/bku/gui/Messages_de.properties b/BKUCommonGUI/src/main/resources/at/gv/egiz/bku/gui/Messages_de.properties
index 72da80ce..5d3def07 100644
--- a/BKUCommonGUI/src/main/resources/at/gv/egiz/bku/gui/Messages_de.properties
+++ b/BKUCommonGUI/src/main/resources/at/gv/egiz/bku/gui/Messages_de.properties
@@ -23,8 +23,10 @@ title.welcome=<html>Willkommen</html>
 title.insertcard=<html>Keine B\u00fcrgerkarte gefunden</html>
 title.cardnotsupported=<html>Die Karte wird nicht unterst\u00fctzt</html>
 title.verify.pin=<html>Karte wird gelesen</html>
-title.sign=<html>Signatur erstellen</html>
+title.sign=<html>{0,choice,1#Signatur|1<{0} Signaturen} erstellen</html>
+title.bulksign=<html>Stapelsignatur erstellen</html>
 title.verify.pinpad=<html>PIN eingeben und best\u00E4tigen</html>
+title.overrule.pinpad=<html>Tastatureingabe freigeben</html>
 title.error=<html>Fehler</html>
 title.warning=<html>Achtung</html>
 title.entry.timeout=<html>Zeit\u00fcberschreitung</html>
@@ -41,11 +43,13 @@ windowtitle.help=Hilfe zur B\u00fcrgerkarte
 # removed message.* prefix to reuse keys as help keys
 welcome=<html>Bitte warten...</html>
 wait=<html>Bitte warten...</html>
+bulksign=<html>Signiere Dokument {0} von {1}</html>
 cardnotsupported=<html>Bitte die B\u00fcrgerkarte in den Kartenleser stecken</html>
 insertcard=<html>Bitte die B\u00fcrgerkarte in den Kartenleser stecken</html>
 enterpin=<html>{0} eingeben</html>
 enterpin.pinpad=<html>PIN am Kartenleser eingeben und best\u00E4tigen</html>
 enterpin.pinpad.direct=<html>{0} ({1} stellig) am Kartenleser eingeben und best\u00E4tigen</html>
+overrule.pinpad=<html>Die Stapelsignatur unterst\u00ftzt keine Pin Eingabe am Kartenleser.</html>
 hashdatalink=<html><a href=\"anzeige\">Signaturdaten anzeigen</a></html>
 hashdatalink.tiny=<html><a href=\"anzeige\">Signaturdaten</a></html>
 hashdatalink.focus=<html><a href=\"anzeige\">[Signaturdaten anzeigen]</a></html>
diff --git a/BKUCommonGUI/src/test/java/at/gv/egiz/bku/gui/BKUGUIWorker.java b/BKUCommonGUI/src/test/java/at/gv/egiz/bku/gui/BKUGUIWorker.java
index 36e5ea0d..6866f51d 100644
--- a/BKUCommonGUI/src/test/java/at/gv/egiz/bku/gui/BKUGUIWorker.java
+++ b/BKUCommonGUI/src/test/java/at/gv/egiz/bku/gui/BKUGUIWorker.java
@@ -120,7 +120,7 @@ public class BKUGUIWorker implements Runnable {
 //                    signedRefs.add(signedRef4);
 //                    signedRefs.add(signedRef4);
 //                    signedRefs = Collections.singletonList(signedRef1);
-        gui.showSecureViewer(signedRefs, returnListener, "return");
+        gui.showSecureViewer(signedRefs, returnListener, "return", null);
       }
     };
 
-- 
cgit v1.2.3