From 54aa4703e3d66c5b1a63b8d925fd4c9c1766687c Mon Sep 17 00:00:00 2001 From: clemenso Date: Wed, 28 Jan 2009 19:40:11 +0000 Subject: activation git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@291 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- .../gv/egiz/marshal/NamespacePrefixMapperImpl.java | 86 ++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 utils/src/main/java/at/gv/egiz/marshal/NamespacePrefixMapperImpl.java (limited to 'utils/src/main/java/at/gv/egiz/marshal/NamespacePrefixMapperImpl.java') diff --git a/utils/src/main/java/at/gv/egiz/marshal/NamespacePrefixMapperImpl.java b/utils/src/main/java/at/gv/egiz/marshal/NamespacePrefixMapperImpl.java new file mode 100644 index 00000000..a08c1188 --- /dev/null +++ b/utils/src/main/java/at/gv/egiz/marshal/NamespacePrefixMapperImpl.java @@ -0,0 +1,86 @@ +/* + * 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.marshal; + +//import com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper; +import com.sun.xml.bind.marshaller.NamespacePrefixMapper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @author Clemens Orthacker + */ +public class NamespacePrefixMapperImpl extends NamespacePrefixMapper { + + private static final Log log = LogFactory.getLog(NamespacePrefixMapperImpl.class); + + @Override + public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) { + + if (log.isTraceEnabled()) { + log.trace("prefix for namespace " + namespaceUri + " requested"); + } + if ("http://www.w3.org/2001/XMLSchema-instance".equals(namespaceUri)) { + return "xsi"; + } + + if ("http://www.w3.org/2000/09/xmldsig#".equals(namespaceUri)) { + return "dsig"; + } + + if ("http://www.buergerkarte.at/namespaces/securitylayer/1.2#".equals(namespaceUri)) { + return "sl"; + } + + if ("http://www.buergerkarte.at/cardchannel".equals(namespaceUri)) { + return "cc"; + } + + if ("http://www.w3.org/2001/04/xmldsig-more#".equals(namespaceUri)) { + return "ecdsa"; + } + + if ("http://reference.e-government.gv.at/namespace/persondata/20020228#".equals(namespaceUri)) { + return "pr"; + } + + if ("urn:oasis:names:tc:SAML:1.0:assertion".equals(namespaceUri)) { + return "saml"; + } + + if ("http://uri.etsi.org/01903/v1.1.1#".equals(namespaceUri)) { + return "xades"; + } + + return suggestion; + } + + /** + * Returns a list of namespace URIs that should be declared + * at the root element. + *

+ * By default, the JAXB RI produces namespace declarations only when + * they are necessary, only at where they are used. Because of this + * lack of look-ahead, sometimes the marshaller produces a lot of + * namespace declarations that look redundant to human eyes. For example, + */ + @Override + public String[] getPreDeclaredNamespaceUris() { + return new String[]{ "http://www.buergerkarte.at/namespaces/securitylayer/1.2#" }; + } +} -- cgit v1.2.3 From 6576428966f1e3d688269a407b072fb01f9f7647 Mon Sep 17 00:00:00 2001 From: clemenso Date: Thu, 26 Feb 2009 19:39:00 +0000 Subject: 1.1 candidate (activation) git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@309 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- .../gv/egiz/bku/online/applet/AppletBKUWorker.java | 9 +- .../java/at/gv/egiz/bku/gui/PINManagementGUI.java | 519 ++++- .../at/gv/egiz/bku/gui/PINManagementGUIFacade.java | 47 +- .../java/at/gv/egiz/bku/gui/PINSpecRenderer.java | 39 + .../java/at/gv/egiz/bku/gui/PINStatusProvider.java | 32 - .../java/at/gv/egiz/bku/gui/PINStatusRenderer.java | 63 + .../at/gv/egiz/bku/gui/PINStatusTableModel.java | 60 + .../bku/online/applet/PINManagementApplet.java | 3 +- .../bku/online/applet/PINManagementBKUWorker.java | 82 +- .../smccstal/ext/PINManagementRequestHandler.java | 331 ++++ .../bku/smccstal/ext/PINMgmtRequestHandler.java | 93 - .../gv/egiz/bku/gui/ActivationMessages.properties | 28 +- .../egiz/bku/gui/ActivationMessages_en.properties | 30 +- .../test/java/at/gv/egiz/bku/gui/BKUGUIWorker.java | 4 +- BKUAppletExt/src/test/resources/appletTest.html | 2 +- .../main/java/at/gv/egiz/bku/gui/BKUGUIImpl.java | 8 +- .../main/java/at/gv/egiz/bku/gui/PinDocument.java | 28 +- .../at/gv/egiz/bku/gui/Messages.properties | 4 +- .../test/java/at/gv/egiz/bku/gui/BKUGUIWorker.java | 2 +- .../at/gv/egiz/stal/ext/ActivatePINRequest.java | 28 - .../java/at/gv/egiz/stal/ext/ChangePINRequest.java | 28 - .../at/gv/egiz/stal/ext/PINManagementRequest.java | 31 + .../at/gv/egiz/stal/ext/PINManagementResponse.java | 28 + .../at/gv/egiz/stal/ext/UnblockPINRequest.java | 28 - .../egiz/bku/slcommands/impl/xsect/DataObject.java | 2078 ++++++++++---------- .../egiz/bku/slcommands/impl/xsect/Signature.java | 5 +- .../bku/slcommands/impl/xsect/SignatureTest.java | 1505 +++++++------- .../egiz/bku/slcommands/impl/TransformsInfo_2.xml | 397 ++++ smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java | 25 +- .../at/gv/egiz/smcc/AbstractSignatureCard.java | 10 + smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java | 30 +- .../src/main/java/at/gv/egiz/smcc/STARCOSCard.java | 25 +- smcc/src/main/java/at/gv/egiz/smcc/SWCard.java | 14 +- .../main/java/at/gv/egiz/smcc/SignatureCard.java | 5 +- .../at/gv/egiz/bku/smccstal/AbstractBKUWorker.java | 3 + .../java/at/gv/egiz/smcc/AbstractSMCCSTALTest.java | 9 +- .../java/at/gv/egiz/marshal/NamespacePrefix.java | 34 + .../gv/egiz/marshal/NamespacePrefixMapperImpl.java | 16 +- .../at/gv/egiz/slbinding/RedirectEventFilter.java | 389 ++-- .../gv/egiz/slbinding/impl/TransformsInfoType.java | 1 + .../at/gv/egiz/slbinding/impl/XMLContentType.java | 2 +- .../org/w3/_2000/_09/xmldsig_/ObjectFactory.java | 1 + .../org/w3/_2000/_09/xmldsig_/TransformsType.java | 2 +- .../java/at/gv/egiz/slbinding/RedirectTest.java | 29 +- .../CreateXMLSignatureRequest02.xml_redirect.txt | 5 +- 45 files changed, 3813 insertions(+), 2299 deletions(-) create mode 100644 BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINSpecRenderer.java delete mode 100644 BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINStatusProvider.java create mode 100644 BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINStatusRenderer.java create mode 100644 BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINStatusTableModel.java create mode 100644 BKUAppletExt/src/main/java/at/gv/egiz/bku/smccstal/ext/PINManagementRequestHandler.java delete mode 100644 BKUAppletExt/src/main/java/at/gv/egiz/bku/smccstal/ext/PINMgmtRequestHandler.java delete mode 100644 STALExt/src/main/java/at/gv/egiz/stal/ext/ActivatePINRequest.java delete mode 100644 STALExt/src/main/java/at/gv/egiz/stal/ext/ChangePINRequest.java create mode 100644 STALExt/src/main/java/at/gv/egiz/stal/ext/PINManagementRequest.java create mode 100644 STALExt/src/main/java/at/gv/egiz/stal/ext/PINManagementResponse.java delete mode 100644 STALExt/src/main/java/at/gv/egiz/stal/ext/UnblockPINRequest.java create mode 100644 bkucommon/src/test/resources/at/gv/egiz/bku/slcommands/impl/TransformsInfo_2.xml create mode 100644 utils/src/main/java/at/gv/egiz/marshal/NamespacePrefix.java (limited to 'utils/src/main/java/at/gv/egiz/marshal/NamespacePrefixMapperImpl.java') diff --git a/BKUApplet/src/main/java/at/gv/egiz/bku/online/applet/AppletBKUWorker.java b/BKUApplet/src/main/java/at/gv/egiz/bku/online/applet/AppletBKUWorker.java index 5a57ef18..8c1bd2bd 100644 --- a/BKUApplet/src/main/java/at/gv/egiz/bku/online/applet/AppletBKUWorker.java +++ b/BKUApplet/src/main/java/at/gv/egiz/bku/online/applet/AppletBKUWorker.java @@ -195,11 +195,16 @@ public class AppletBKUWorker extends AbstractBKUWorker implements Runnable { } } + /** + * + * @param err_code + * @param ex if not null, the message will be appended as parameter to the error message + */ protected void showErrorDialog(String err_code, Exception ex) { actionCommandList.clear(); actionCommandList.add("ok"); - gui.showErrorDialog(err_code, - new Object[]{ex.getMessage()}, this, "ok"); + Object[] params = (ex != null) ? new Object[] { ex.getMessage() } : null; + gui.showErrorDialog(err_code, params, this, "ok"); try { waitForAction(); } catch (InterruptedException e) { diff --git a/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUI.java b/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUI.java index 8acf051e..8eef8aea 100644 --- a/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUI.java +++ b/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUI.java @@ -17,15 +17,28 @@ package at.gv.egiz.bku.gui; +import at.gv.egiz.smcc.PINSpec; import java.awt.Container; +import java.awt.Cursor; +import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; import java.net.URL; +import java.text.MessageFormat; import java.util.Locale; +import java.util.Map; import javax.swing.GroupLayout; import javax.swing.JButton; import javax.swing.JLabel; +import javax.swing.JPasswordField; +import javax.swing.JScrollPane; +import javax.swing.JTable; import javax.swing.LayoutStyle; +import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; /** * TODO pull out ResourceBundle to common superclass for activationGUI and pinMgmtGUI @@ -33,9 +46,10 @@ import javax.swing.SwingUtilities; */ public class PINManagementGUI extends ActivationGUI implements PINManagementGUIFacade { - public static final String BUTTON_ACTIVATE = "button.activate"; - public static final String BUTTON_UNBLOCK = "button.unblock"; - public static final String BUTTON_CHANGE = "button.change"; + /** remember the pinfield to return to worker */ + protected JPasswordField oldPinField; + /** remember the pinSpec to return to worker */ + protected PINSpec pinSpec; public PINManagementGUI(Container contentPane, Locale locale, @@ -46,12 +60,31 @@ public class PINManagementGUI extends ActivationGUI implements PINManagementGUIF } @Override - public void showPINManagementDialog(final PINStatusProvider pinStatusProvider, - final ActionListener activateListener, final String activateCmd, - final ActionListener changeListener, final String changeCmd, - final ActionListener unblockListener, final String unblockCmd, - final ActionListener cancelListener, final String cancelCmd) { -// try { + public char[] getOldPin() { + if (oldPinField != null) { + char[] pin = oldPinField.getPassword(); + oldPinField = null; + return pin; + } + return null; + } + + @Override + public PINSpec getSelectedPIN() { + return pinSpec; + } + + @Override + public void showPINManagementDialog(final Map pins, + final ActionListener activateListener, + final String activateCmd, + final String changeCmd, + final String unblockCmd, + final ActionListener cancelListener, + final String cancelCmd) { + + log.debug("scheduling PIN managment dialog"); + SwingUtilities.invokeLater(new Runnable() { @Override @@ -68,13 +101,76 @@ public class PINManagementGUI extends ActivationGUI implements PINManagementGUIF if (renderHeaderPanel) { titleLabel.setText(cardmgmtMessages.getString(TITLE_PINMGMT)); - mgmtLabel.setText(cardmgmtMessages.getString(MESSAGE_PINMGMT)); + String infoPattern = cardmgmtMessages.getString(MESSAGE_PINMGMT); + mgmtLabel.setText(MessageFormat.format(infoPattern, pins.size())); } else { mgmtLabel.setText(cardmgmtMessages.getString(TITLE_PINMGMT)); } + final PINStatusTableModel tableModel = new PINStatusTableModel(pins); + final JTable pinStatusTable = new JTable(tableModel); + pinStatusTable.setDefaultRenderer(PINSpec.class, new PINSpecRenderer()); + pinStatusTable.setDefaultRenderer(STATUS.class, new PINStatusRenderer(cardmgmtMessages)); + pinStatusTable.setTableHeader(null); - + pinStatusTable.addMouseMotionListener(new MouseMotionAdapter() { + + @Override + public void mouseMoved(MouseEvent e) { + if (pinStatusTable.columnAtPoint(e.getPoint()) == 0) { + pinStatusTable.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + } else { + pinStatusTable.setCursor(Cursor.getDefaultCursor()); + } + } + }); + + final JButton activateButton = new JButton(); + activateButton.setFont(activateButton.getFont().deriveFont(activateButton.getFont().getStyle() & ~java.awt.Font.BOLD)); + activateButton.addActionListener(activateListener); + + pinStatusTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + pinStatusTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + + @Override + public void valueChanged(final ListSelectionEvent e) { + //invoke later to allow thread to paint selection background + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + ListSelectionModel lsm = (ListSelectionModel) e.getSource(); + int selectionIdx = lsm.getMinSelectionIndex(); + if (selectionIdx >= 0) { + pinSpec = (PINSpec) tableModel.getValueAt(selectionIdx, 0); + STATUS status = (STATUS) tableModel.getValueAt(selectionIdx, 1); + + if (status == STATUS.NOT_ACTIV) { + activateButton.setText(cardmgmtMessages.getString(BUTTON_ACTIVATE)); + activateButton.setEnabled(true); + activateButton.setActionCommand(activateCmd); + } else if (status == STATUS.BLOCKED) { + activateButton.setText(cardmgmtMessages.getString(BUTTON_UNBLOCK)); + activateButton.setEnabled(true); + activateButton.setActionCommand(unblockCmd); + } else if (status == STATUS.ACTIV) { + activateButton.setText(cardmgmtMessages.getString(BUTTON_CHANGE)); + activateButton.setEnabled(true); + activateButton.setActionCommand(changeCmd); + } else { + activateButton.setText(cardmgmtMessages.getString(BUTTON_ACTIVATE)); + activateButton.setEnabled(false); + } + } + } + }); + } + }); + + //select first entry + pinStatusTable.getSelectionModel().setSelectionInterval(0, 0); + + JScrollPane pinStatusScrollPane = new JScrollPane(pinStatusTable); GroupLayout mainPanelLayout = new GroupLayout(mainPanel); mainPanel.setLayout(mainPanelLayout); @@ -91,30 +187,16 @@ public class PINManagementGUI extends ActivationGUI implements PINManagementGUIF .addComponent(helpLabel); } - mainPanelLayout.setHorizontalGroup(messageHorizontal); - mainPanelLayout.setVerticalGroup(messageVertical); + mainPanelLayout.setHorizontalGroup( + mainPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addGroup(messageHorizontal) + .addComponent(pinStatusScrollPane, 0, 0, Short.MAX_VALUE)); - - JButton activateButton = new JButton(); - activateButton.setFont(activateButton.getFont().deriveFont(activateButton.getFont().getStyle() & ~java.awt.Font.BOLD)); - activateButton.setText(cardmgmtMessages.getString(BUTTON_ACTIVATE)); - activateButton.setEnabled(true);//false); - activateButton.setActionCommand(activateCmd); - activateButton.addActionListener(activateListener); - - JButton changeButton = new JButton(); - changeButton.setFont(activateButton.getFont().deriveFont(activateButton.getFont().getStyle() & ~java.awt.Font.BOLD)); - changeButton.setText(cardmgmtMessages.getString(BUTTON_CHANGE)); - changeButton.setEnabled(false); - changeButton.setActionCommand(changeCmd); - changeButton.addActionListener(changeListener); - - JButton unblockButton = new JButton(); - unblockButton.setFont(activateButton.getFont().deriveFont(activateButton.getFont().getStyle() & ~java.awt.Font.BOLD)); - unblockButton.setText(cardmgmtMessages.getString(BUTTON_UNBLOCK)); - unblockButton.setEnabled(false); - unblockButton.setActionCommand(unblockCmd); - unblockButton.addActionListener(unblockListener); + mainPanelLayout.setVerticalGroup( + mainPanelLayout.createSequentialGroup() + .addGroup(messageVertical) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pinStatusScrollPane, 0, 0, pinStatusTable.getPreferredSize().height+3)); JButton cancelButton = new JButton(); cancelButton.setFont(cancelButton.getFont().deriveFont(cancelButton.getFont().getStyle() & ~java.awt.Font.BOLD)); @@ -129,30 +211,377 @@ public class PINManagementGUI extends ActivationGUI implements PINManagementGUIF .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(activateButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(changeButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(unblockButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) .addComponent(cancelButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE); GroupLayout.Group buttonVertical = buttonPanelLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) .addComponent(activateButton) - .addComponent(changeButton) - .addComponent(unblockButton) .addComponent(cancelButton); buttonPanelLayout.setHorizontalGroup(buttonHorizontal); buttonPanelLayout.setVerticalGroup(buttonVertical); contentPanel.validate(); - } }); + } + + @Override + public void showActivatePINDialog(final PINSpec pin, + final ActionListener okListener, final String okCommand, + final ActionListener cancelListener, final String cancelCommand) { + log.debug("scheduling activate pin dialog"); + showPINDialog(false, pin, okListener, okCommand, cancelListener, cancelCommand); + } + + + private void showPINDialog(final boolean changePin, final PINSpec pinSpec, + final ActionListener okListener, final String okCommand, + final ActionListener cancelListener, final String cancelCommand) { + + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + + String HELP_TOPIC, TITLE, MESSAGE_MGMT; + if (changePin) { + log.debug("show change pin dialog"); + HELP_TOPIC = HELP_PINMGMT; + TITLE = TITLE_CHANGE_PIN; + MESSAGE_MGMT = MESSAGE_CHANGE_PIN; + } else { + log.debug("show activate pin dialog"); + HELP_TOPIC = HELP_PINMGMT; + TITLE = TITLE_ACTIVATE_PIN; + MESSAGE_MGMT = MESSAGE_ACTIVATE_PIN; + oldPinField = null; + } + + mainPanel.removeAll(); + buttonPanel.removeAll(); + + helpListener.setHelpTopic(HELP_TOPIC); + + JLabel mgmtLabel = new JLabel(); + mgmtLabel.setFont(mgmtLabel.getFont().deriveFont(mgmtLabel.getFont().getStyle() & ~java.awt.Font.BOLD)); + + if (renderHeaderPanel) { + titleLabel.setText(cardmgmtMessages.getString(TITLE)); + String mgmtPattern = cardmgmtMessages.getString(MESSAGE_MGMT); + if (shortText) { + mgmtLabel.setText(MessageFormat.format(mgmtPattern, "PIN")); + } else { + mgmtLabel.setText(MessageFormat.format(mgmtPattern, pinSpec.getLocalizedName())); + } + } else { + mgmtLabel.setText(cardmgmtMessages.getString(TITLE)); + } + + JButton okButton = new JButton(); + okButton.setFont(okButton.getFont().deriveFont(okButton.getFont().getStyle() & ~java.awt.Font.BOLD)); + okButton.setText(messages.getString(BUTTON_OK)); + okButton.setEnabled(false); + okButton.setActionCommand(okCommand); + okButton.addActionListener(okListener); + + JLabel pinLabel = new JLabel(); + pinLabel.setFont(pinLabel.getFont().deriveFont(pinLabel.getFont().getStyle() & ~java.awt.Font.BOLD)); + String pinLabelPattern = (changePin) ? cardmgmtMessages.getString(LABEL_NEW_PIN) : messages.getString(LABEL_PIN); + pinLabel.setText(MessageFormat.format(pinLabelPattern, new Object[]{pinSpec.getLocalizedName()})); + + final JPasswordField repeatPinField = new JPasswordField(); + pinField = new JPasswordField(); + pinField.setText(""); + pinField.setDocument(new PINDocument(pinSpec, null)); + pinField.setActionCommand(okCommand); + pinField.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + if (pinField.getPassword().length >= pinSpec.getMinLength()) { + repeatPinField.requestFocusInWindow(); + } + } + }); + JLabel repeatPinLabel = new JLabel(); + repeatPinLabel.setFont(pinLabel.getFont()); + String repeatPinLabelPattern = cardmgmtMessages.getString(LABEL_REPEAT_PIN); + repeatPinLabel.setText(MessageFormat.format(repeatPinLabelPattern, new Object[]{pinSpec.getLocalizedName()})); + + repeatPinField.setText(""); + repeatPinField.setDocument(new PINDocument(pinSpec, okButton, pinField.getDocument())); + repeatPinField.setActionCommand(okCommand); + repeatPinField.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + if (pinField.getPassword().length >= pinSpec.getMinLength()) { + okListener.actionPerformed(e); + } + } + }); + + JLabel oldPinLabel = null; + if (changePin) { + oldPinLabel = new JLabel(); + oldPinLabel.setFont(oldPinLabel.getFont().deriveFont(oldPinLabel.getFont().getStyle() & ~java.awt.Font.BOLD)); + String oldPinLabelPattern = cardmgmtMessages.getString(LABEL_OLD_PIN); + oldPinLabel.setText(MessageFormat.format(oldPinLabelPattern, new Object[]{pinSpec.getLocalizedName()})); + + oldPinField = new JPasswordField(); + oldPinField.setText(""); + oldPinField.setDocument(new PINDocument(pinSpec, null)); + oldPinField.setActionCommand(okCommand); + oldPinField.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + if (oldPinField.getPassword().length >= pinSpec.getMinLength()) { + pinField.requestFocusInWindow(); + } + } + }); + } + + JLabel pinsizeLabel = new JLabel(); + pinsizeLabel.setFont(pinsizeLabel.getFont().deriveFont(pinsizeLabel.getFont().getStyle() & ~java.awt.Font.BOLD, pinsizeLabel.getFont().getSize()-2)); + String pinsizePattern = messages.getString(LABEL_PINSIZE); + String pinSize = String.valueOf(pinSpec.getMinLength()); + if (pinSpec.getMinLength() != pinSpec.getMaxLength()) { + pinSize += "-" + pinSpec.getMaxLength(); + } + pinsizeLabel.setText(MessageFormat.format(pinsizePattern, new Object[]{pinSize})); + + GroupLayout mainPanelLayout = new GroupLayout(mainPanel); + mainPanel.setLayout(mainPanelLayout); + + GroupLayout.SequentialGroup infoHorizontal = mainPanelLayout.createSequentialGroup() + .addComponent(mgmtLabel); + GroupLayout.ParallelGroup infoVertical = mainPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(mgmtLabel); + + if (!renderHeaderPanel) { + infoHorizontal + .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, 0, Short.MAX_VALUE) + .addComponent(helpLabel); + infoVertical + .addComponent(helpLabel); + } -// } catch (Exception ex) { -// log.error(ex.getMessage(), ex); -// showErrorDialog(ERR_UNKNOWN_WITH_PARAM, new Object[] {ex.getMessage()}); -// } + GroupLayout.ParallelGroup pinHorizontal = mainPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING); + GroupLayout.SequentialGroup pinVertical = mainPanelLayout.createSequentialGroup(); + + if (pinLabelPos == PinLabelPosition.ABOVE) { + if (changePin) { + pinHorizontal + .addComponent(oldPinLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addComponent(oldPinField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE); + pinVertical + .addComponent(oldPinLabel) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(oldPinField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED); + } + pinHorizontal + .addComponent(pinLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addComponent(pinField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(repeatPinLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addComponent(repeatPinField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(mainPanelLayout.createSequentialGroup() + .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, 0, Short.MAX_VALUE) + .addComponent(pinsizeLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)); + pinVertical + .addComponent(pinLabel) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pinField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(repeatPinLabel) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(repeatPinField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pinsizeLabel); + } else { + if (changePin) { + pinHorizontal + .addGroup(mainPanelLayout.createSequentialGroup() + .addGroup(mainPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(oldPinLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addComponent(pinLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addComponent(repeatPinLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addGroup(mainPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(oldPinField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(pinField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(repeatPinField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))); + + pinVertical + .addGroup(mainPanelLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(oldPinLabel) + .addComponent(oldPinField)) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED); + } else { + pinHorizontal + .addGroup(mainPanelLayout.createSequentialGroup() + .addGroup(mainPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(pinLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addComponent(repeatPinLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addGroup(mainPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(pinField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(repeatPinField, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))); + + } + pinHorizontal + .addGroup(mainPanelLayout.createSequentialGroup() + .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, 0, Short.MAX_VALUE) + .addComponent(pinsizeLabel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)); + pinVertical + .addGroup(mainPanelLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(pinLabel) + .addComponent(pinField)) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addGroup(mainPanelLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(repeatPinLabel) + .addComponent(repeatPinField)) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pinsizeLabel); + } + + mainPanelLayout.setHorizontalGroup( + mainPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addGroup(infoHorizontal) + .addGroup(pinHorizontal)); + + mainPanelLayout.setVerticalGroup( + mainPanelLayout.createSequentialGroup() + .addGroup(infoVertical) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pinVertical)); + + GroupLayout buttonPanelLayout = new GroupLayout(buttonPanel); + buttonPanel.setLayout(buttonPanelLayout); + + GroupLayout.SequentialGroup buttonHorizontal = buttonPanelLayout.createSequentialGroup() + .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(okButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE); + GroupLayout.Group buttonVertical; + + JButton cancelButton = new JButton(); + cancelButton.setFont(cancelButton.getFont().deriveFont(cancelButton.getFont().getStyle() & ~java.awt.Font.BOLD)); + cancelButton.setText(messages.getString(BUTTON_CANCEL)); + cancelButton.setActionCommand(cancelCommand); + cancelButton.addActionListener(cancelListener); + + buttonHorizontal + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cancelButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE); + buttonVertical = buttonPanelLayout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(okButton) + .addComponent(cancelButton); + + buttonPanelLayout.setHorizontalGroup(buttonHorizontal); + buttonPanelLayout.setVerticalGroup(buttonVertical); + + if (oldPinField != null) { + oldPinField.requestFocusInWindow(); + } else { + pinField.requestFocusInWindow(); + } + contentPanel.validate(); + + } + }); + } + + @Override + public void showChangePINDialog(final PINSpec pin, + final ActionListener okListener, final String okCommand, + final ActionListener cancelListener, final String cancelCommand) { + + log.debug("scheduling change pin dialog"); + showPINDialog(true, pin, okListener, okCommand, cancelListener, cancelCommand); + } + + @Override + public void showUnblockPINDialog(final PINSpec pin, + final ActionListener okListener, final String okCommand, + final ActionListener cancelListener, final String cancelCommand) { + + log.debug("scheduling unblock PIN dialog"); + + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + + log.debug("show unblock PIN dialog"); + + log.error("unblock pin not supported"); + + mainPanel.removeAll(); + buttonPanel.removeAll(); + + if (renderHeaderPanel) { + titleLabel.setText(messages.getString(TITLE_ERROR)); + } + + helpListener.setHelpTopic(HELP_PINMGMT); + + String errorMsgPattern = cardmgmtMessages.getString(ERR_UNBLOCK); + String errorMsg = MessageFormat.format(errorMsgPattern, pin.getLocalizedName()); + + JLabel errorMsgLabel = new JLabel(); + errorMsgLabel.setFont(errorMsgLabel.getFont().deriveFont(errorMsgLabel.getFont().getStyle() & ~java.awt.Font.BOLD)); + errorMsgLabel.setText(errorMsg); + + GroupLayout mainPanelLayout = new GroupLayout(mainPanel); + mainPanel.setLayout(mainPanelLayout); + + GroupLayout.ParallelGroup mainHorizontal = mainPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING); + GroupLayout.SequentialGroup mainVertical = mainPanelLayout.createSequentialGroup(); + + if (!renderHeaderPanel) { + JLabel errorTitleLabel = new JLabel(); + errorTitleLabel.setFont(errorTitleLabel.getFont().deriveFont(errorTitleLabel.getFont().getStyle() | java.awt.Font.BOLD)); + errorTitleLabel.setText(messages.getString(TITLE_ERROR)); + errorTitleLabel.setForeground(ERROR_COLOR); + + mainHorizontal + .addGroup(mainPanelLayout.createSequentialGroup() + .addComponent(errorTitleLabel) + .addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED, 0, Short.MAX_VALUE) + .addComponent(helpLabel)); + mainVertical + .addGroup(mainPanelLayout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addComponent(errorTitleLabel) + .addComponent(helpLabel)); + } + + mainPanelLayout.setHorizontalGroup(mainHorizontal + .addComponent(errorMsgLabel)); + mainPanelLayout.setVerticalGroup(mainVertical + .addComponent(errorMsgLabel)); + + JButton okButton = new JButton(); + okButton.setFont(okButton.getFont().deriveFont(okButton.getFont().getStyle() & ~java.awt.Font.BOLD)); + okButton.setText(messages.getString(BUTTON_OK)); + okButton.setActionCommand(cancelCommand); + okButton.addActionListener(cancelListener); + + GroupLayout buttonPanelLayout = new GroupLayout(buttonPanel); + buttonPanel.setLayout(buttonPanelLayout); + + buttonPanelLayout.setHorizontalGroup( + buttonPanelLayout.createSequentialGroup() + .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(okButton, GroupLayout.PREFERRED_SIZE, buttonSize, GroupLayout.PREFERRED_SIZE)); + buttonPanelLayout.setVerticalGroup( + buttonPanelLayout.createSequentialGroup() + .addComponent(okButton)); + + contentPanel.validate(); + } + }); } diff --git a/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUIFacade.java b/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUIFacade.java index 3d653fab..2a8f28d2 100644 --- a/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUIFacade.java +++ b/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINManagementGUIFacade.java @@ -17,7 +17,9 @@ package at.gv.egiz.bku.gui; +import at.gv.egiz.smcc.PINSpec; import java.awt.event.ActionListener; +import java.util.Map; /** * @@ -27,8 +29,49 @@ public interface PINManagementGUIFacade extends BKUGUIFacade { public static final String HELP_PINMGMT = "help.pin.mgmt"; public static final String TITLE_PINMGMT = "title.pin.mgmt"; + public static final String TITLE_ACTIVATE_PIN = "title.activate.pin"; + public static final String TITLE_CHANGE_PIN = "title.change.pin"; + public static final String TITLE_UNBLOCK_PIN = "title.unblock.pin"; public static final String MESSAGE_PINMGMT = "message.pin.mgmt"; - - public void showPINManagementDialog(PINStatusProvider pinStatusProvider, ActionListener activateListener, String activateCmd, ActionListener changeListener, String changeCmd, ActionListener unblockListener, String unblockCmd, ActionListener cancelListener, String cancelCmd); + public static final String MESSAGE_ACTIVATE_PIN = "message.activate.pin"; + public static final String MESSAGE_CHANGE_PIN = "message.change.pin"; + public static final String MESSAGE_UNBLOCK_PIN = "message.unblock.pin"; + public static final String LABEL_OLD_PIN = "label.old.pin"; + public static final String LABEL_NEW_PIN = "label.new.pin"; + public static final String LABEL_REPEAT_PIN = "label.repeat.pin"; + public static final String ERR_ACTIVATE = "err.activate"; + public static final String ERR_CHANGE = "err.change"; + public static final String ERR_UNBLOCK = "err.unblock"; + + public static final String BUTTON_ACTIVATE = "button.activate"; + public static final String BUTTON_UNBLOCK = "button.unblock"; + public static final String BUTTON_CHANGE = "button.change"; + + public static final String STATUS_ACTIVE = "status.active"; + public static final String STATUS_BLOCKED = "status.blocked"; + public static final String STATUS_NOT_ACTIVE = "status.not.active"; + public static final String STATUS_UNKNOWN = "status.unknown"; + + public enum STATUS { ACTIV, NOT_ACTIV, BLOCKED, UNKNOWN }; + + public void showPINManagementDialog(Map pins, + ActionListener activateListener, String activateCmd, String changeCmd, String unblockCmd, + ActionListener cancelListener, String cancelCmd); + + public void showActivatePINDialog(PINSpec pin, + ActionListener okListener, String okCmd, + ActionListener cancelListener, String cancelCmd); + + public void showChangePINDialog(PINSpec pin, + ActionListener okListener, String okCmd, + ActionListener cancelListener, String cancelCmd); + + public void showUnblockPINDialog(PINSpec pin, + ActionListener okListener, String okCmd, + ActionListener cancelListener, String cancelCmd); + + public char[] getOldPin(); + + public PINSpec getSelectedPIN(); } diff --git a/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINSpecRenderer.java b/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINSpecRenderer.java new file mode 100644 index 00000000..e3d73e1f --- /dev/null +++ b/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINSpecRenderer.java @@ -0,0 +1,39 @@ +/* + * 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.gui; + +import at.gv.egiz.smcc.PINSpec; +import javax.swing.table.DefaultTableCellRenderer; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @author Clemens Orthacker + */ +public class PINSpecRenderer extends DefaultTableCellRenderer { + + private static final Log log = LogFactory.getLog(PINSpecRenderer.class); + + @Override + protected void setValue(Object value) { + PINSpec pinSpec = (PINSpec) value; + super.setText(pinSpec.getLocalizedName()); + } + +} diff --git a/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINStatusProvider.java b/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINStatusProvider.java deleted file mode 100644 index 73fa0920..00000000 --- a/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINStatusProvider.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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.gui; - -import at.gv.egiz.smcc.SignatureCardException; - -/** - * - * @author Clemens Orthacker - */ -public interface PINStatusProvider { - - public enum STATUS { ACTIV, NOT_ACTIV, BLOCKED }; - - public STATUS getPINStatus(int pin) throws SignatureCardException; - -} diff --git a/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINStatusRenderer.java b/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINStatusRenderer.java new file mode 100644 index 00000000..2f8852ff --- /dev/null +++ b/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINStatusRenderer.java @@ -0,0 +1,63 @@ +/* + * 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.gui; + +import at.gv.egiz.bku.gui.PINManagementGUIFacade.STATUS; +import java.awt.Color; +import java.awt.Font; +import java.util.ResourceBundle; +import javax.swing.table.DefaultTableCellRenderer; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @author Clemens Orthacker + */ +public class PINStatusRenderer extends DefaultTableCellRenderer { + + private static final Log log = LogFactory.getLog(PINStatusRenderer.class); + + public static final Color RED = new Color(0.9f, 0.0f, 0.0f); + public static final Color GREEN = new Color(0.0f, 0.8f, 0.0f); + protected ResourceBundle messages; + + public PINStatusRenderer(ResourceBundle messages) { + this.messages = messages; + } + + @Override + protected void setValue(Object value) { + STATUS pinStatus = (STATUS) value; + super.setFont(super.getFont().deriveFont(super.getFont().getStyle() | Font.BOLD)); + + if (pinStatus == STATUS.NOT_ACTIV) { + super.setForeground(RED); + super.setText("" + messages.getString(PINManagementGUIFacade.STATUS_NOT_ACTIVE) + ""); + } else if (pinStatus == STATUS.ACTIV) { + super.setForeground(GREEN); + super.setText("" + messages.getString(PINManagementGUIFacade.STATUS_ACTIVE) + ""); + } else if (pinStatus == STATUS.BLOCKED) { + super.setForeground(RED); + super.setText("" + messages.getString(PINManagementGUIFacade.STATUS_BLOCKED) + ""); + } else { + super.setForeground(Color.BLACK); + super.setText("" + messages.getString(PINManagementGUIFacade.STATUS_UNKNOWN) + ""); + } + } +} diff --git a/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINStatusTableModel.java b/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINStatusTableModel.java new file mode 100644 index 00000000..feaa5072 --- /dev/null +++ b/BKUAppletExt/src/main/java/at/gv/egiz/bku/gui/PINStatusTableModel.java @@ -0,0 +1,60 @@ +/* + * 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.gui; + +import at.gv.egiz.bku.gui.PINManagementGUIFacade.STATUS; +import at.gv.egiz.smcc.PINSpec; +import java.util.Map; +import javax.swing.table.DefaultTableModel; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @author Clemens Orthacker + */ +public class PINStatusTableModel extends DefaultTableModel { + + protected static final Log log = LogFactory.getLog(PINStatusTableModel.class); + protected Class[] types; + + public PINStatusTableModel(Map pinStatuses) { + super(0, 2); + if (pinStatuses == null) { + throw new RuntimeException("pinStatuses must not be null"); + } + log.trace(pinStatuses.size() + " PINs"); + types = new Class[] { PINSpec.class, STATUS.class }; + for (PINSpec pinSpec : pinStatuses.keySet()) { + addRow(new Object[] { pinSpec, pinStatuses.get(pinSpec) }); + } +// PINSpec activePIN = new PINSpec(0, 1, null, "active-PIN", (byte) 0x01); +// PINSpec blockedPIN = new PINSpec(0, 1, null, "blocked-PIN", (byte) 0x01); +// addRow(new Object[] { activePIN, PINStatusProvider.STATUS.ACTIV }); +// addRow(new Object[] { blockedPIN, PINStatusProvider.STATUS.BLOCKED }); + } + + @Override + public Class getColumnClass(int columnIndex) { + return types[columnIndex]; + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + return false; + } +} diff --git a/BKUAppletExt/src/main/java/at/gv/egiz/bku/online/applet/PINManagementApplet.java b/BKUAppletExt/src/main/java/at/gv/egiz/bku/online/applet/PINManagementApplet.java index 72d06618..d948ac03 100644 --- a/BKUAppletExt/src/main/java/at/gv/egiz/bku/online/applet/PINManagementApplet.java +++ b/BKUAppletExt/src/main/java/at/gv/egiz/bku/online/applet/PINManagementApplet.java @@ -19,6 +19,7 @@ package at.gv.egiz.bku.online.applet; import at.gv.egiz.bku.gui.AbstractHelpListener; import at.gv.egiz.bku.gui.BKUGUIFacade; import at.gv.egiz.bku.gui.PINManagementGUI; +import at.gv.egiz.bku.gui.PINManagementGUIFacade; import java.awt.Container; import java.net.URL; import java.util.Locale; @@ -45,6 +46,6 @@ public class PINManagementApplet extends BKUApplet { @Override protected AppletBKUWorker createBKUWorker(BKUApplet applet, BKUGUIFacade gui) { - return new PINManagementBKUWorker(applet, gui); + return new PINManagementBKUWorker(applet, (PINManagementGUIFacade) gui); } } diff --git a/BKUAppletExt/src/main/java/at/gv/egiz/bku/online/applet/PINManagementBKUWorker.java b/BKUAppletExt/src/main/java/at/gv/egiz/bku/online/applet/PINManagementBKUWorker.java index e65d98ca..ffd83e42 100644 --- a/BKUAppletExt/src/main/java/at/gv/egiz/bku/online/applet/PINManagementBKUWorker.java +++ b/BKUAppletExt/src/main/java/at/gv/egiz/bku/online/applet/PINManagementBKUWorker.java @@ -18,35 +18,28 @@ package at.gv.egiz.bku.online.applet; import at.gv.egiz.bku.gui.BKUGUIFacade; import at.gv.egiz.bku.gui.PINManagementGUIFacade; -import at.gv.egiz.bku.smccstal.ext.PINMgmtRequestHandler; +import at.gv.egiz.bku.smccstal.ext.PINManagementRequestHandler; +import at.gv.egiz.stal.ErrorResponse; import at.gv.egiz.stal.STALResponse; -import at.gv.egiz.stal.ext.ActivatePINRequest; -import at.gv.egiz.stal.ext.ChangePINRequest; -import at.gv.egiz.stal.ext.UnblockPINRequest; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.Collection; +import at.gv.egiz.stal.ext.PINManagementRequest; +import at.gv.egiz.stal.ext.PINManagementResponse; import java.util.Collections; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; /** - * + * This BKU Worker does not connect to STAL webservice + * (no Internet connection permitted while activating PINs). + * * @author Clemens Orthacker */ public class PINManagementBKUWorker extends AppletBKUWorker { - protected PINMgmtRequestHandler handler = new PINMgmtRequestHandler(); - protected PINManagementActionListener listener = new PINManagementActionListener(); - - public PINManagementBKUWorker(BKUApplet applet, BKUGUIFacade gui) { + public PINManagementBKUWorker(BKUApplet applet, PINManagementGUIFacade gui) { super(applet, gui); handlerMap.clear(); -// PINMgmtRequestHandler handler = new PINMgmtRequestHandler(); -// addRequestHandler(ActivatePINRequest.class, handler); -// addRequestHandler(ChangePINRequest.class, handler); -// addRequestHandler(UnblockPINRequest.class, handler); + addRequestHandler(PINManagementRequest.class, new PINManagementRequestHandler()); } @Override @@ -54,22 +47,24 @@ public class PINManagementBKUWorker extends AppletBKUWorker { gui.showWelcomeDialog(); try { - - if (waitForCard()) { - gui.showErrorDialog("no card, canceled PIN mgmt dialog", null); + List responses = handleRequest(Collections.singletonList(new PINManagementRequest())); + + if (responses.size() == 1) { + STALResponse response = responses.get(0); + if (response instanceof PINManagementResponse) { + log.debug("PIN management dialog finished"); + } else if (response instanceof ErrorResponse) { + showErrorDialog(BKUGUIFacade.ERR_UNKNOWN, null); + } else { + throw new RuntimeException("Invalid STAL response: " + response.getClass().getName()); + } + } else { + throw new RuntimeException("invalid number of STAL responses: " + responses.size()); } - actionCommandList.clear(); - actionCommandList.add("cancel"); - - ((PINManagementGUIFacade) gui).showPINManagementDialog(handler, - listener, "activate", - listener, "change", - listener, "unblock", - this, "cancel"); - - waitForAction(); - + } catch (RuntimeException ex) { + log.error("unexpected error: " + ex.getMessage(), ex); + showErrorDialog(BKUGUIFacade.ERR_UNKNOWN, null); } catch (Exception ex) { log.error(ex.getMessage(), ex); showErrorDialog(BKUGUIFacade.ERR_UNKNOWN_WITH_PARAM, ex); @@ -82,31 +77,4 @@ public class PINManagementBKUWorker extends AppletBKUWorker { applet.sendRedirect(sessionId); } - protected class PINManagementActionListener implements ActionListener { - - @Override - public void actionPerformed(ActionEvent e) { - try { - String cmd = e.getActionCommand(); - if ("activate".equals(cmd)) { - //create STAL request, call handle(req) - ActivatePINRequest stalReq = new ActivatePINRequest(); - STALResponse stalResp = handler.handleRequest(stalReq); - gui.showErrorDialog(BKUGUIFacade.ERR_UNKNOWN_WITH_PARAM, new Object[]{"debug"}, this, "back"); - } else if ("change".equals(cmd)) { - } else if ("unblock".equals(cmd)) { - } else if ("back".equals(cmd)) { - - ((PINManagementGUIFacade) gui).showPINManagementDialog(handler, - this, "activate", - this, "change", - this, "unblock", - PINManagementBKUWorker.this, "cancel"); - - } - } catch (InterruptedException ex) { - log.fatal(ex); - } - } } -} diff --git a/BKUAppletExt/src/main/java/at/gv/egiz/bku/smccstal/ext/PINManagementRequestHandler.java b/BKUAppletExt/src/main/java/at/gv/egiz/bku/smccstal/ext/PINManagementRequestHandler.java new file mode 100644 index 00000000..fcef3191 --- /dev/null +++ b/BKUAppletExt/src/main/java/at/gv/egiz/bku/smccstal/ext/PINManagementRequestHandler.java @@ -0,0 +1,331 @@ +/* + * 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.ext; + +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.PINSpec; +import at.gv.egiz.smcc.SignatureCardException; +import at.gv.egiz.smcc.util.SMCCHelper; +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; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +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; + +/** + * + * @author Clemens Orthacker + */ +public class PINManagementRequestHandler extends AbstractRequestHandler { + + public static final String ERR_NOPIN_SELECTED = "err.no.pin.selected"; + protected static final Log log = LogFactory.getLog(PINManagementRequestHandler.class); + +// protected ResourceBundle messages; + +// public PINManagementRequestHandler(ResourceBundle messages) { +// this.messages = messages; +// } + @Override + public STALResponse handleRequest(STALRequest request) throws InterruptedException { + if (request instanceof PINManagementRequest) { + + PINManagementGUIFacade gui = (PINManagementGUIFacade) this.gui; + + showPINManagementDialog(gui); + + while (true) { + + waitForAction(); + + if ("cancel".equals(actionCommand)) { + return new PINManagementResponse(); + } else if ("back".equals(actionCommand)) { + showPINManagementDialog(gui); + } else { + PINSpec selectedPIN = gui.getSelectedPIN(); + + if (selectedPIN == null) { + throw new RuntimeException("no PIN selected for activation/change"); + } + + if ("activate_enterpin".equals(actionCommand)) { + gui.showActivatePINDialog(selectedPIN, this, "activate", this, "back"); + } else if ("change_enterpin".equals(actionCommand)) { + gui.showChangePINDialog(selectedPIN, this, "change", this, "back"); + } else if ("unblock_enterpuk".equals(actionCommand)) { + gui.showUnblockPINDialog(selectedPIN, this, "unblock", this, "back"); + } else if ("activate".equals(actionCommand)) { + try { + byte[] pin = encodePIN(gui.getPin()); + activatePIN(selectedPIN.getKID(), selectedPIN.getContextAID(), pin); + showPINManagementDialog(gui); + } catch (SignatureCardException ex) { + log.error("failed to activate " + selectedPIN.getLocalizedName() + ": " + ex.getMessage()); + gui.showErrorDialog(PINManagementGUIFacade.ERR_ACTIVATE, + new Object[] {selectedPIN.getLocalizedName()}, + this, "cancel"); + } + } else if ("change".equals(actionCommand)) { + try { + byte[] oldPin = encodePIN(gui.getOldPin()); //new byte[]{(byte) 0x25, (byte) 0x40, (byte) 0x01}; + byte[] pin = encodePIN(gui.getPin()); //new byte[]{(byte) 0x25, (byte) 0x40}; + changePIN(selectedPIN.getKID(), selectedPIN.getContextAID(), oldPin, pin); + showPINManagementDialog(gui); + } catch (SignatureCardException ex) { + log.error("failed to change " + selectedPIN.getLocalizedName() + ": " + ex.getMessage()); + gui.showErrorDialog(PINManagementGUIFacade.ERR_CHANGE, + new Object[] {selectedPIN.getLocalizedName()}, + this, "cancel"); + } + } else if ("unblock".equals(actionCommand)) { + log.error("unblock PIN not implemented"); + gui.showErrorDialog(PINManagementGUIFacade.ERR_UNBLOCK, null, this, "cancel"); + } else { + throw new RuntimeException("unsupported action " + actionCommand); + } + } + } + } else { + log.error("Got unexpected STAL request: " + request); + return new ErrorResponse(1000); + } + } + + @Override + public boolean requireCard() { + return true; + } + + /** + * pin.length < 4bit + * @param kid + * @param contextAID + * @param pin + * @throws at.gv.egiz.smcc.SignatureCardException + */ + private void activatePIN(byte kid, byte[] contextAID, byte[] pin) throws SignatureCardException { + try { + Card icc = card.getCard(); + icc.beginExclusive(); + CardChannel channel = icc.getBasicChannel(); + + if (contextAID != null) { + CommandAPDU selectAPDU = new CommandAPDU(0x00, 0xa4, 0x04, 0x0c, contextAID); + ResponseAPDU responseAPDU = channel.transmit(selectAPDU); + if (responseAPDU.getSW() != 0x9000) { + String msg = "Failed to activate PIN " + SMCCHelper.toString(new byte[]{kid}) + + ": Failed to select AID " + SMCCHelper.toString(contextAID) + + ": " + SMCCHelper.toString(responseAPDU.getBytes()); + log.error(msg); + throw new SignatureCardException(msg); + } + } + + if (pin.length > 7) { + log.error("Invalid PIN"); + throw new SignatureCardException("Invalid PIN"); + } + byte length = (byte) (0x20 | pin.length * 2); + + byte[] apdu = new byte[]{ + (byte) 0x00, (byte) 0x24, (byte) 0x01, kid, (byte) 0x08, + (byte) length, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; + for (int i = 0; i < pin.length; i++) { + apdu[i + 6] = pin[i]; + } + + CommandAPDU verifyAPDU = new CommandAPDU(apdu); + ResponseAPDU responseAPDU = channel.transmit(verifyAPDU); + + if (responseAPDU.getSW() != 0x9000) { + String msg = "Failed to activate PIN " + SMCCHelper.toString(new byte[]{kid}) + ": " + SMCCHelper.toString(responseAPDU.getBytes()); + log.error(msg); + throw new SignatureCardException(msg); + } + + + icc.endExclusive(); + + + } catch (CardException ex) { + log.error("Failed to get PIN status: " + ex.getMessage()); + throw new SignatureCardException("Failed to get PIN status", ex); + } + } + + private void changePIN(byte kid, byte[] contextAID, byte[] oldPIN, byte[] newPIN) throws SignatureCardException { + try { + Card icc = card.getCard(); + icc.beginExclusive(); + CardChannel channel = icc.getBasicChannel(); + + if (contextAID != null) { + CommandAPDU selectAPDU = new CommandAPDU(0x00, 0xa4, 0x04, 0x0c, contextAID); + ResponseAPDU responseAPDU = channel.transmit(selectAPDU); + if (responseAPDU.getSW() != 0x9000) { + String msg = "Failed to change PIN " + SMCCHelper.toString(new byte[]{kid}) + + ": Failed to select AID " + SMCCHelper.toString(contextAID) + + ": " + SMCCHelper.toString(responseAPDU.getBytes()); + log.error(msg); + throw new SignatureCardException(msg); + } + } + + if (oldPIN.length > 7 || newPIN.length > 7) { + log.error("Invalid PIN"); + throw new SignatureCardException("Invalid PIN"); + } + byte oldLength = (byte) (0x20 | oldPIN.length * 2); + byte newLength = (byte) (0x20 | newPIN.length * 2); + + byte[] apdu = new byte[]{ + (byte) 0x00, (byte) 0x24, (byte) 0x00, kid, (byte) 0x10, + oldLength, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + newLength, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; + for (int i = 0; i < oldPIN.length; i++) { + apdu[i + 6] = oldPIN[i]; + } + for (int i = 0; i < newPIN.length; i++) { + apdu[i + 14] = newPIN[i]; + } + + CommandAPDU verifyAPDU = new CommandAPDU(apdu); + ResponseAPDU responseAPDU = channel.transmit(verifyAPDU); + + if (responseAPDU.getSW() != 0x9000) { + String msg = "Failed to change PIN " + SMCCHelper.toString(new byte[]{kid}) + ": " + SMCCHelper.toString(responseAPDU.getBytes()); + log.error(msg); + throw new SignatureCardException(msg); + } + + + icc.endExclusive(); + + + } catch (CardException ex) { + log.error("Failed to get PIN status: " + ex.getMessage()); + throw new SignatureCardException("Failed to get PIN status", ex); + } + } + + public Map getPINStatuses() throws SignatureCardException { + try { + Card icc = card.getCard(); + icc.beginExclusive(); + CardChannel channel = icc.getBasicChannel(); + + HashMap pinStatuses = new HashMap(); + List pins = card.getPINSpecs(); + + //select DF_SichereSignatur 00 A4 04 0C 08 D0 40 00 00 17 00 12 01 +// CommandAPDU selectAPDU = new CommandAPDU(new byte[]{(byte) 0x00, (byte) 0xa4, (byte) 0x04, (byte) 0x0c, (byte) 0x08, +// (byte) 0xd0, (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, (byte) 0x12, (byte) 0x01}); +// ResponseAPDU rAPDU = channel.transmit(selectAPDU); +// log.debug("SELECT FILE DF_SichereSignatur: " + SMCCHelper.toString(rAPDU.getBytes())); + + //select DF_SIG DF 70 +// CommandAPDU selectAPDU = new CommandAPDU(new byte[]{(byte) 0x00, (byte) 0xa4, (byte) 0x00, (byte) 0x0c, (byte) 0x02, +// (byte) 0xdf, (byte) 0x70 }); +// ResponseAPDU rAPDU = channel.transmit(selectAPDU); +// log.debug("SELECT FILE DF_SIG: " + SMCCHelper.toString(rAPDU.getBytes())); + + //select DF_DEC DF 71 +// CommandAPDU selectAPDU = new CommandAPDU(new byte[]{(byte) 0x00, (byte) 0xa4, (byte) 0x04, (byte) 0x0c, (byte) 0x08, +// (byte) 0xd0, (byte) 0x40, (byte) 0x00, (byte) 0x00, (byte) 0x17, (byte) 0x00, (byte) 0x12, (byte) 0x01}); +// ResponseAPDU rAPDU = channel.transmit(selectAPDU); +// log.debug("SELECT FILE DF_SichereSignatur: " + SMCCHelper.toString(rAPDU.getBytes())); + + for (PINSpec pinSpec : pins) { + byte kid = pinSpec.getKID(); + byte[] contextAID = pinSpec.getContextAID(); + + if (contextAID != null) { + CommandAPDU selectAPDU = new CommandAPDU(0x00, 0xa4, 0x04, 0x0c, contextAID); + ResponseAPDU responseAPDU = channel.transmit(selectAPDU); + if (responseAPDU.getSW() != 0x9000) { + String msg = "Failed to activate PIN " + SMCCHelper.toString(new byte[]{kid}) + + ": Failed to select AID " + SMCCHelper.toString(contextAID) + + ": " + SMCCHelper.toString(responseAPDU.getBytes()); + log.error(msg); + throw new SignatureCardException(msg); + } + } + + CommandAPDU verifyAPDU = new CommandAPDU(new byte[]{(byte) 0x00, (byte) 0x20, (byte) 00, kid}); + ResponseAPDU responseAPDU = channel.transmit(verifyAPDU); + + STATUS status = STATUS.UNKNOWN; + if (responseAPDU.getSW() == 0x6984) { + status = STATUS.NOT_ACTIV; + } else if (responseAPDU.getSW() == 0x63c0) { + status = STATUS.BLOCKED; + } else if (responseAPDU.getSW1() == 0x63) { + status = STATUS.ACTIV; + } + if (log.isDebugEnabled()) { + log.debug("PIN " + pinSpec.getLocalizedName() + " status: " + SMCCHelper.toString(responseAPDU.getBytes())); + } + + pinStatuses.put(pinSpec, status); + } + icc.endExclusive(); + + return pinStatuses; + + } catch (CardException ex) { + log.error("Failed to get PIN status: " + ex.getMessage()); + throw new SignatureCardException("Failed to get PIN status", ex); + } + } + + private byte[] encodePIN(char[] pinChars) { + int length = (int) Math.ceil(pinChars.length/2); + byte[] pin = new byte[length]; + for (int i = 0; i < length; i++) { + pin[i] = (byte) (16*Character.digit(pinChars[i*2], 16) + Character.digit(pinChars[i*2+1], 16)); + } + log.trace("***** " + SMCCHelper.toString(pin) + " ******"); + return pin; + } + + private void showPINManagementDialog(PINManagementGUIFacade gui) { + try { + Map pins = getPINStatuses(); + gui.showPINManagementDialog(pins, + this, "activate_enterpin", "change_enterpin", "unblock_enterpuk", + this, "cancel"); + } catch (SignatureCardException ex) { + gui.showErrorDialog(BKUGUIFacade.ERR_UNKNOWN_WITH_PARAM, + new Object[]{"FAILED TO GET PIN STATUSES: " + ex.getMessage()}, + this, "cancel"); + } + } +} diff --git a/BKUAppletExt/src/main/java/at/gv/egiz/bku/smccstal/ext/PINMgmtRequestHandler.java b/BKUAppletExt/src/main/java/at/gv/egiz/bku/smccstal/ext/PINMgmtRequestHandler.java deleted file mode 100644 index b2d34ff2..00000000 --- a/BKUAppletExt/src/main/java/at/gv/egiz/bku/smccstal/ext/PINMgmtRequestHandler.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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.ext; - -import at.gv.egiz.bku.gui.PINStatusProvider; -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.ActivatePINRequest; -import at.gv.egiz.stal.ext.ChangePINRequest; -import at.gv.egiz.stal.ext.UnblockPINRequest; -import java.util.logging.Level; -import java.util.logging.Logger; -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; - -/** - * - * @author Clemens Orthacker - */ -public class PINMgmtRequestHandler extends AbstractRequestHandler implements PINStatusProvider { - - protected static final Log log = LogFactory.getLog(PINMgmtRequestHandler.class); - - @Override - public STALResponse handleRequest(STALRequest request) throws InterruptedException { - if (request instanceof ActivatePINRequest) { - log.error("not implemented yet"); - return new ErrorResponse(1000); - - } else if (request instanceof ChangePINRequest) { - log.error("not implemented yet"); - return new ErrorResponse(1000); - - } else if (request instanceof UnblockPINRequest) { - log.error("not implemented yet"); - return new ErrorResponse(1000); - - } else { - log.error("Got unexpected STAL request: " + request); - return new ErrorResponse(1000); - } - } - - @Override - public boolean requireCard() { - return true; - } - - @Override - public STATUS getPINStatus(int pin) throws SignatureCardException { - try { - Card icc = card.getCard(); - icc.beginExclusive(); - CardChannel channel = icc.getBasicChannel(); - CommandAPDU verifyAPDU = new CommandAPDU(new byte[] {(byte) 0x00} ); - ResponseAPDU responseAPDU = channel.transmit(verifyAPDU); - byte sw1 = (byte) responseAPDU.getSW1(); - byte[] sw = new byte[] { - (byte) (0xFF & responseAPDU.getSW1()), - (byte) (0xFF & responseAPDU.getSW2()) }; - - icc.endExclusive(); - return STATUS.ACTIV; - } catch (CardException ex) { - log.error("Failed to get PIN status: " + ex.getMessage()); - throw new SignatureCardException("Failed to get PIN status", ex); - } - } - -} diff --git a/BKUAppletExt/src/main/resources/at/gv/egiz/bku/gui/ActivationMessages.properties b/BKUAppletExt/src/main/resources/at/gv/egiz/bku/gui/ActivationMessages.properties index 469af15f..e51044af 100644 --- a/BKUAppletExt/src/main/resources/at/gv/egiz/bku/gui/ActivationMessages.properties +++ b/BKUAppletExt/src/main/resources/at/gv/egiz/bku/gui/ActivationMessages.properties @@ -15,10 +15,34 @@ title.activation=Aktivierung title.pin.mgmt=PIN Verwaltung -message.pin.mgmt=under construction +title.activate.pin=PIN Aktivieren +title.change.pin=PIN \u00C4ndern +title.unblock.pin=PIN Entsperren + +message.pin.mgmt=Die Karte verf\u00FCgt \u00FCber {0} PINs +message.activate.pin={0} eingeben und best\u00E4tigen +message.change.pin={0} eingeben und best\u00E4tigen +message.unblock.pin=PUK zu {0} eingeben + label.activation=e-card Aktivierungsprozess label.activation.step=Schritt {0} label.activation.idle=Warte auf Server... +label.old.pin=Alte {0}: +label.new.pin=Neue {0}: +label.repeat.pin=Best\u00E4tigung: + button.activate=Aktivieren button.change=\u00C4ndern -button.unblock=Entsperren \ No newline at end of file +button.unblock=Entsperren + +help.activation=help.activation +help.pin.mgmt=help.pin.mgmt + +err.activate=Beim Aktivieren der {0} trat ein Fehler auf. +err.change=Beim \u00C4ndern der {0} trat ein Fehler auf. +err.unblock=Das Entsperren der {0} wird nicht unterst\u00FCtzt. + +status.not.active=NICHT AKTIV +status.active=AKTIV +status.blocked=GESPERRT +status.unknown=UNBEKANNT diff --git a/BKUAppletExt/src/main/resources/at/gv/egiz/bku/gui/ActivationMessages_en.properties b/BKUAppletExt/src/main/resources/at/gv/egiz/bku/gui/ActivationMessages_en.properties index 16ac7d0b..1cf4a102 100644 --- a/BKUAppletExt/src/main/resources/at/gv/egiz/bku/gui/ActivationMessages_en.properties +++ b/BKUAppletExt/src/main/resources/at/gv/egiz/bku/gui/ActivationMessages_en.properties @@ -13,12 +13,36 @@ # See the License for the specific language governing permissions and # limitations under the License. -title.activation=Aktivation +title.activation=Activation title.pin.mgmt=PIN Management -message.pin.mgmt=under construction +title.activate.pin=Activate PIN +title.change.pin=Change PIN +title.unblock.pin=Unblock PIN + +message.pin.mgmt=The smartcard has {0} PINs +message.activate.pin=Enter and confirm {0} +message.change.pin=Enter and confirm {0} +message.unblock.pin=Enter PUK for {0} + label.activation=e-card activation process label.activation.step=Step {0} label.activation.idle=Wait for server... +label.old.pin=Old {0}: +label.new.pin=New {0}: +label.repeat.pin=Confirmation: + button.activate=Activate button.change=Change -button.unblock=Unblock \ No newline at end of file +button.unblock=Unblock + +help.activation=help.activation +help.pin.mgmt=help.pin.mgmt + +err.activate=An error occured during activation of {0}. +err.change=An error occured during changing of {0}. +err.unblock=Unblocking of {0} is not supported. + +status.not.active=Not active +status.active=Active +status.blocked=Blocked +status.unknown=Unknown diff --git a/BKUAppletExt/src/test/java/at/gv/egiz/bku/gui/BKUGUIWorker.java b/BKUAppletExt/src/test/java/at/gv/egiz/bku/gui/BKUGUIWorker.java index 669a63fc..ef8c87e4 100644 --- a/BKUAppletExt/src/test/java/at/gv/egiz/bku/gui/BKUGUIWorker.java +++ b/BKUAppletExt/src/test/java/at/gv/egiz/bku/gui/BKUGUIWorker.java @@ -25,8 +25,6 @@ import at.gv.egiz.stal.HashDataInput; import at.gv.egiz.stal.impl.ByteArrayHashDataInput; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.io.ByteArrayInputStream; -import java.io.InputStream; import java.util.ArrayList; import java.util.List; @@ -46,7 +44,7 @@ public class BKUGUIWorker implements Runnable { public void run() { try { - final PINSpec signPinSpec = new PINSpec(6, 10, "[0-9]", "Signatur-PIN"); + final PINSpec signPinSpec = new PINSpec(6, 10, "[0-9]", "Signatur-PIN", (byte)0x00, null); final ActionListener cancelListener = new ActionListener() { diff --git a/BKUAppletExt/src/test/resources/appletTest.html b/BKUAppletExt/src/test/resources/appletTest.html index 9add4309..813ee1f0 100644 --- a/BKUAppletExt/src/test/resources/appletTest.html +++ b/BKUAppletExt/src/test/resources/appletTest.html @@ -19,7 +19,7 @@

+ width=270 height=180> 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 f564c07a..1d5a2cf4 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 @@ -38,9 +38,6 @@ import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.ResourceBundle; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.CellRendererPane; import javax.swing.GroupLayout; import javax.swing.ImageIcon; import javax.swing.JButton; @@ -51,7 +48,6 @@ import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JScrollPane; import javax.swing.JTable; -import javax.swing.JTextField; import javax.swing.LayoutStyle; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; @@ -1064,7 +1060,9 @@ public class BKUGUIImpl implements BKUGUIFacade { @Override public char[] getPin() { if (pinField != null) { - return pinField.getPassword(); + char[] pin = pinField.getPassword(); + pinField = null; + return pin; } return null; } diff --git a/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/PinDocument.java b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/PinDocument.java index 2054ae86..87b636f0 100644 --- a/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/PinDocument.java +++ b/BKUCommonGUI/src/main/java/at/gv/egiz/bku/gui/PinDocument.java @@ -22,6 +22,7 @@ import java.util.regex.Pattern; import javax.swing.JButton; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; +import javax.swing.text.Document; import javax.swing.text.PlainDocument; /** @@ -30,9 +31,10 @@ import javax.swing.text.PlainDocument; */ class PINDocument extends PlainDocument { - private PINSpec pinSpec; - private Pattern pinPattern; - private JButton enterButton; + protected PINSpec pinSpec; + protected Pattern pinPattern; + protected JButton enterButton; + protected Document compareTo; public PINDocument(PINSpec pinSpec, JButton enterButton) { this.pinSpec = pinSpec; @@ -44,6 +46,11 @@ class PINDocument extends PlainDocument { this.enterButton = enterButton; } + public PINDocument(PINSpec pinSpec, JButton enterButton, Document compareTo) { + this(pinSpec, enterButton); + this.compareTo = compareTo; + } + @Override public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { if (pinSpec.getMaxLength() < 0 || pinSpec.getMaxLength() >= (getLength() + str.length())) { @@ -58,12 +65,23 @@ class PINDocument extends PlainDocument { super.insertString(offs, str, a); } } - enterButton.setEnabled(getLength() >= pinSpec.getMinLength()); + if (enterButton != null) { + enterButton.setEnabled(getLength() >= pinSpec.getMinLength() && compare()); + } } @Override public void remove(int offs, int len) throws BadLocationException { super.remove(offs, len); - enterButton.setEnabled(getLength() >= pinSpec.getMinLength()); + if (enterButton != null) { + enterButton.setEnabled(getLength() >= pinSpec.getMinLength() && compare()); + } + } + + private boolean compare() throws BadLocationException { + if (compareTo == null) { + return true; + } + return compareTo.getText(0, compareTo.getLength()).equals(getText(0, getLength())); } } \ No newline at end of file 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 8436a730..1e0bc9f5 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 @@ -20,7 +20,7 @@ title.cardnotsupported=Die Karte wird nicht unterst\u00FCtzt title.cardpin=Karte wird gelesen title.sign=Signatur erstellen title.error=Fehler -title.retry=Falscher PIN +title.retry=Falsche PIN title.wait=Bitte warten title.hashdata=Signaturdaten windowtitle.save=Signaturdaten speichern @@ -84,7 +84,7 @@ help.cardnotsupported=Nicht unterst\u00FCtzte B\u00FCrgerkarte help.insertcard=Keine B\u00FCrgerkarte im Kartenleser help.cardpin=Pineingabe help.signpin=Signatur-Pineingabe -help.retry=Falscher Pin +help.retry=Falsche Pin help.hashdata=Signierte Inhalte help.hashdatalist=Signierte Inhalte help.hashdataviewer=Anzeige signierter Inhalte \ No newline at end of file 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 73aaab46..08ecaa7f 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 @@ -46,7 +46,7 @@ public class BKUGUIWorker implements Runnable { public void run() { // try { - final PINSpec signPinSpec = new PINSpec(6, 10, "[0-9]", "Signatur-PIN"); + final PINSpec signPinSpec = new PINSpec(6, 10, "[0-9]", "Signatur-PIN", (byte) 0x81, null); final ActionListener cancelListener = new ActionListener() { diff --git a/STALExt/src/main/java/at/gv/egiz/stal/ext/ActivatePINRequest.java b/STALExt/src/main/java/at/gv/egiz/stal/ext/ActivatePINRequest.java deleted file mode 100644 index f2039388..00000000 --- a/STALExt/src/main/java/at/gv/egiz/stal/ext/ActivatePINRequest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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.stal.ext; - -import at.gv.egiz.stal.STALRequest; - -/** - * - * @author Clemens Orthacker - */ -public class ActivatePINRequest extends STALRequest { - -} diff --git a/STALExt/src/main/java/at/gv/egiz/stal/ext/ChangePINRequest.java b/STALExt/src/main/java/at/gv/egiz/stal/ext/ChangePINRequest.java deleted file mode 100644 index ea508146..00000000 --- a/STALExt/src/main/java/at/gv/egiz/stal/ext/ChangePINRequest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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.stal.ext; - -import at.gv.egiz.stal.STALRequest; - -/** - * - * @author Clemens Orthacker - */ -public class ChangePINRequest extends STALRequest { - -} diff --git a/STALExt/src/main/java/at/gv/egiz/stal/ext/PINManagementRequest.java b/STALExt/src/main/java/at/gv/egiz/stal/ext/PINManagementRequest.java new file mode 100644 index 00000000..87c53e24 --- /dev/null +++ b/STALExt/src/main/java/at/gv/egiz/stal/ext/PINManagementRequest.java @@ -0,0 +1,31 @@ +/* + * 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.stal.ext; + +import at.gv.egiz.stal.STALRequest; + +/** + * Dummy STAL request to trigger PIN Management. (no proper STAL requests + * for PIN activation, unblocking) + * + * + * @author Clemens Orthacker + */ +public class PINManagementRequest extends STALRequest { + +} diff --git a/STALExt/src/main/java/at/gv/egiz/stal/ext/PINManagementResponse.java b/STALExt/src/main/java/at/gv/egiz/stal/ext/PINManagementResponse.java new file mode 100644 index 00000000..b8b90604 --- /dev/null +++ b/STALExt/src/main/java/at/gv/egiz/stal/ext/PINManagementResponse.java @@ -0,0 +1,28 @@ +/* + * 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.stal.ext; + +import at.gv.egiz.stal.STALResponse; + +/** + * + * @author Clemens Orthacker + */ +public class PINManagementResponse extends STALResponse { + +} diff --git a/STALExt/src/main/java/at/gv/egiz/stal/ext/UnblockPINRequest.java b/STALExt/src/main/java/at/gv/egiz/stal/ext/UnblockPINRequest.java deleted file mode 100644 index 543de31c..00000000 --- a/STALExt/src/main/java/at/gv/egiz/stal/ext/UnblockPINRequest.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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.stal.ext; - -import at.gv.egiz.stal.STALRequest; - -/** - * - * @author Clemens Orthacker - */ -public class UnblockPINRequest extends STALRequest { - -} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/DataObject.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/DataObject.java index ae4918ce..b64306aa 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/DataObject.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/DataObject.java @@ -14,98 +14,105 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package at.gv.egiz.bku.slcommands.impl.xsect; - -import iaik.xml.crypto.dom.DOMCryptoContext; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.SequenceInputStream; -import java.io.StringWriter; -import java.io.UnsupportedEncodingException; -import java.net.URISyntaxException; -import java.nio.charset.Charset; -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; +package at.gv.egiz.bku.slcommands.impl.xsect; + +import iaik.xml.crypto.dom.DOMCryptoContext; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.SequenceInputStream; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.xml.crypto.MarshalException; -import javax.xml.crypto.dom.DOMStructure; -import javax.xml.crypto.dsig.CanonicalizationMethod; -import javax.xml.crypto.dsig.DigestMethod; -import javax.xml.crypto.dsig.Reference; -import javax.xml.crypto.dsig.Transform; -import javax.xml.crypto.dsig.XMLObject; -import javax.xml.crypto.dsig.spec.TransformParameterSpec; -import javax.xml.crypto.dsig.spec.XPathFilter2ParameterSpec; -import javax.xml.crypto.dsig.spec.XPathType; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.w3c.dom.DOMConfiguration; -import org.w3c.dom.DOMException; -import org.w3c.dom.Document; -import org.w3c.dom.DocumentFragment; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.Text; -import org.w3c.dom.bootstrap.DOMImplementationRegistry; -import org.w3c.dom.ls.DOMImplementationLS; -import org.w3c.dom.ls.LSException; -import org.w3c.dom.ls.LSInput; -import org.w3c.dom.ls.LSOutput; -import org.w3c.dom.ls.LSParser; -import org.w3c.dom.ls.LSSerializer; - -import at.buergerkarte.namespaces.securitylayer._1.Base64XMLLocRefOptRefContentType; -import at.buergerkarte.namespaces.securitylayer._1.DataObjectInfoType; -import at.buergerkarte.namespaces.securitylayer._1.MetaInfoType; -import at.buergerkarte.namespaces.securitylayer._1.TransformsInfoType; -import at.gv.egiz.bku.binding.HttpUtil; -import at.gv.egiz.bku.slexceptions.SLCommandException; -import at.gv.egiz.bku.slexceptions.SLRequestException; -import at.gv.egiz.bku.slexceptions.SLRuntimeException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.crypto.MarshalException; +import javax.xml.crypto.dom.DOMStructure; +import javax.xml.crypto.dsig.CanonicalizationMethod; +import javax.xml.crypto.dsig.DigestMethod; +import javax.xml.crypto.dsig.Reference; +import javax.xml.crypto.dsig.Transform; +import javax.xml.crypto.dsig.XMLObject; +import javax.xml.crypto.dsig.spec.TransformParameterSpec; +import javax.xml.crypto.dsig.spec.XPathFilter2ParameterSpec; +import javax.xml.crypto.dsig.spec.XPathType; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3._2000._09.xmldsig_.TransformType; +import org.w3._2000._09.xmldsig_.TransformsType; +import org.w3c.dom.DOMConfiguration; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentFragment; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.Text; +import org.w3c.dom.bootstrap.DOMImplementationRegistry; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSException; +import org.w3c.dom.ls.LSInput; +import org.w3c.dom.ls.LSOutput; +import org.w3c.dom.ls.LSParser; +import org.w3c.dom.ls.LSSerializer; + +import at.buergerkarte.namespaces.securitylayer._1.Base64XMLLocRefOptRefContentType; +import at.buergerkarte.namespaces.securitylayer._1.DataObjectInfoType; +import at.buergerkarte.namespaces.securitylayer._1.MetaInfoType; +import at.buergerkarte.namespaces.securitylayer._1.TransformsInfoType; +import at.gv.egiz.bku.binding.HttpUtil; +import at.gv.egiz.bku.slexceptions.SLCommandException; +import at.gv.egiz.bku.slexceptions.SLRequestException; +import at.gv.egiz.bku.slexceptions.SLRuntimeException; import at.gv.egiz.bku.slexceptions.SLViewerException; -import at.gv.egiz.bku.utils.urldereferencer.StreamData; -import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer; +import at.gv.egiz.bku.utils.urldereferencer.StreamData; +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer; import at.gv.egiz.bku.viewer.ValidationException; import at.gv.egiz.bku.viewer.Validator; import at.gv.egiz.bku.viewer.ValidatorFactory; -import at.gv.egiz.dom.DOMUtils; -import at.gv.egiz.slbinding.impl.XMLContentType; - -/** - * This class represents a DataObject of an XML-Signature - * created by the security layer command CreateXMLSignature. - * - * @author mcentner - */ -public class DataObject { - - /** - * Logging facility. - */ - private static Log log = LogFactory.getLog(DataObject.class); - - /** - * DOM Implementation. - */ - private static final String DOM_LS_3_0 = "LS 3.0"; - - /** - * The array of the default preferred MIME type order. - */ - private static final String[] DEFAULT_PREFFERED_MIME_TYPES = - new String[] { +import at.gv.egiz.dom.DOMUtils; +import at.gv.egiz.marshal.NamespacePrefix; +import at.gv.egiz.marshal.NamespacePrefixMapperImpl; +import at.gv.egiz.slbinding.impl.XMLContentType; +import javax.xml.namespace.NamespaceContext; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +/** + * This class represents a DataObject of an XML-Signature + * created by the security layer command CreateXMLSignature. + * + * @author mcentner + */ +public class DataObject { + + /** + * Logging facility. + */ + private static Log log = LogFactory.getLog(DataObject.class); + + /** + * DOM Implementation. + */ + private static final String DOM_LS_3_0 = "LS 3.0"; + + /** + * The array of the default preferred MIME type order. + */ + private static final String[] DEFAULT_PREFFERED_MIME_TYPES = + new String[] { "text/plain", - "application/xhtml+xml" + "application/xhtml+xml" }; /** @@ -149,87 +156,87 @@ public class DataObject { validMimeTypes = mediaTypes; } - /** - * The DOM implementation used. - */ - private DOMImplementationLS domImplLS; - - /** - * The signature context. - */ - private SignatureContext ctx; - - /** - * The Reference for this DataObject. - */ - private XSECTReference reference; - - /** - * The XMLObject for this DataObject. - */ - private XMLObject xmlObject; - - /** - * The MIME-Type of the digest input. - */ - private String mimeType; - - /** - * An optional description of the digest input. - */ - private String description; - - /** - * Creates a new instance. - * - * @param document the document of the target signature - */ - public DataObject(SignatureContext signatureContext) { - this.ctx = signatureContext; - - DOMImplementationRegistry registry; - try { - registry = DOMImplementationRegistry.newInstance(); - } catch (Exception e) { - log.error("Failed to get DOMImplementationRegistry.", e); - throw new SLRuntimeException("Failed to get DOMImplementationRegistry."); - } - - domImplLS = (DOMImplementationLS) registry.getDOMImplementation(DOM_LS_3_0); - if (domImplLS == null) { - log.error("Failed to get DOMImplementation " + DOM_LS_3_0); - throw new SLRuntimeException("Failed to get DOMImplementation " + DOM_LS_3_0); - } - - } - - /** - * @return the reference - */ - public Reference getReference() { - return reference; - } - - /** - * @return the xmlObject - */ - public XMLObject getXmlObject() { - return xmlObject; - } - - /** - * @return the mimeType - */ - public String getMimeType() { - return mimeType; - } - - /** - * @return the description - */ - public String getDescription() { - return description; - } + /** + * The DOM implementation used. + */ + private DOMImplementationLS domImplLS; + + /** + * The signature context. + */ + private SignatureContext ctx; + + /** + * The Reference for this DataObject. + */ + private XSECTReference reference; + + /** + * The XMLObject for this DataObject. + */ + private XMLObject xmlObject; + + /** + * The MIME-Type of the digest input. + */ + private String mimeType; + + /** + * An optional description of the digest input. + */ + private String description; + + /** + * Creates a new instance. + * + * @param document the document of the target signature + */ + public DataObject(SignatureContext signatureContext) { + this.ctx = signatureContext; + + DOMImplementationRegistry registry; + try { + registry = DOMImplementationRegistry.newInstance(); + } catch (Exception e) { + log.error("Failed to get DOMImplementationRegistry.", e); + throw new SLRuntimeException("Failed to get DOMImplementationRegistry."); + } + + domImplLS = (DOMImplementationLS) registry.getDOMImplementation(DOM_LS_3_0); + if (domImplLS == null) { + log.error("Failed to get DOMImplementation " + DOM_LS_3_0); + throw new SLRuntimeException("Failed to get DOMImplementation " + DOM_LS_3_0); + } + + } + + /** + * @return the reference + */ + public Reference getReference() { + return reference; + } + + /** + * @return the xmlObject + */ + public XMLObject getXmlObject() { + return xmlObject; + } + + /** + * @return the mimeType + */ + public String getMimeType() { + return mimeType; + } + + /** + * @return the description + */ + public String getDescription() { + return description; + } public void validateHashDataInput() throws SLViewerException { @@ -293,823 +300,920 @@ public class DataObject { } } - - /** - * Configures this DataObject with the information provided within the given - * sl:DataObjectInfo. - * - * @param dataObjectInfo - * the sl:DataObjectInfo - * - * @throws SLCommandException - * if configuring this DataObject with the information provided in - * the sl:DataObjectInfo fails. - * @throws SLRequestException - * if the information provided in the sl:DataObjectInfo - * does not conform to the security layer specification. - * @throws NullPointerException - * if dataObjectInfo is null - */ - public void setDataObjectInfo(DataObjectInfoType dataObjectInfo) throws SLCommandException, SLRequestException { - - Base64XMLLocRefOptRefContentType dataObject = dataObjectInfo.getDataObject(); - String structure = dataObjectInfo.getStructure(); - - // select and unmarshal an appropriate transformation path if provided - // and set the final data meta information - XSECTTransforms transforms = createTransformsAndSetFinalDataMetaInfo(dataObjectInfo.getTransformsInfo()); - - if ("enveloping".equals(structure)) { - - // configure this DataObject as an enveloped DataObject - setEnvelopedDataObject(dataObject, transforms); - - } else if ("detached".equals(structure)) { - - // configure this DataObject as an detached DataObject - setDetachedDataObject(dataObject, transforms); - - } - // other values are not allowed by the schema and are therefore ignored - - } - - /** - * Configures this DataObject as an enveloped DataObject with the information - * provided within the given sl:DataObject. - * - * @param dataObject - * the sl:DataObject - * @param transforms - * an optional Transforms element (may be - * null) - * - * @throws SLCommandException - * if configuring this DataObject with the information provided in - * the sl:DataObject fails. - * @throws SLRequestException - * if the information provided in the sl:DataObject - * does not conform to the security layer specification. - * @throws NullPointerException - * if dataObject is null - */ - private void setEnvelopedDataObject( - Base64XMLLocRefOptRefContentType dataObject, XSECTTransforms transforms) - throws SLCommandException, SLRequestException { - - String reference = dataObject.getReference(); - if (reference == null) { - // - // case A - // - // The Reference attribute is not used; the content of sl:DataObject represents the data object. - // If the data object is XML-coded (the sl:XMLContent element is used in sl:DataObject), then it - // must be incorporated in the signature structure as parsed XML. - // - - if (dataObject.getBase64Content() != null) { - - log.debug("Adding DataObject (Base64Content) without a reference URI."); - - // create XMLObject - XMLObject xmlObject = createXMLObject(new ByteArrayInputStream(dataObject.getBase64Content())); - - setXMLObjectAndReferenceBase64(xmlObject, transforms); - - } else if (dataObject.getXMLContent() != null) { - - log.debug("Adding DataObject (XMLContent) without a reference URI."); - - // create XMLObject - DocumentFragment content = parseDataObject((XMLContentType) dataObject.getXMLContent()); - XMLObject xmlObject = createXMLObject(content); - - setXMLObjectAndReferenceXML(xmlObject, transforms); - - } else if (dataObject.getLocRefContent() != null) { - - log.debug("Adding DataObject (LocRefContent) without a reference URI."); - - setEnvelopedDataObject(dataObject.getLocRefContent(), transforms); - - } else { - - // not allowed - log.info("XML structure of the command request contains an " + - "invalid combination of optional elements or attributes. " + - "DataObject of structure='enveloped' without a reference must contain content."); - throw new SLRequestException(3003); - - } - - } else { - - if (dataObject.getBase64Content() == null && - dataObject.getXMLContent() == null && - dataObject.getLocRefContent() == null) { - - // - // case B - // - // The Reference attribute contains a URI that must be resolved by the - // Citizen Card Environment to obtain the data object. - // The content of sl:DataObject remains empty - // - - log.debug("Adding DataObject from reference URI '" + reference + "'."); - - setEnvelopedDataObject(reference, transforms); - - } else { - - // not allowed - log.info("XML structure of the command request contains an " + - "invalid combination of optional elements or attributes. " + - "DataObject of structure='enveloped' with reference must not contain content."); - throw new SLRequestException(3003); - - } - - - } - - } - - /** - * Configures this DataObject as an enveloped DataObject with the content to - * be dereferenced from the given reference. - * - * @param reference - * the reference URI - * @param transforms - * an optional Transforms element (may be - * null) - * - * @throws SLCommandException - * if dereferencing the given reference fails, or if - * configuring this DataObject with the data dereferenced from the - * given reference fails. - * @throws NullPointerException - * if reference is null - */ - private void setEnvelopedDataObject(String reference, XSECTTransforms transforms) throws SLCommandException { - - if (reference == null) { - throw new NullPointerException("Argument 'reference' must not be null."); - } - - // dereference URL - URLDereferencer dereferencer = URLDereferencer.getInstance(); - - StreamData streamData; - try { - streamData = dereferencer.dereference(reference, ctx.getDereferencerContext()); - } catch (IOException e) { - log.info("Failed to dereference XMLObject from '" + reference + "'.", e); - throw new SLCommandException(4110); - } - - Node childNode; - - String contentType = streamData.getContentType(); - if (contentType.startsWith("text/xml")) { - - // If content type is text/xml parse content. - String charset = HttpUtil.getCharset(contentType, true); - - Document doc = parseDataObject(streamData.getStream(), charset); - - childNode = doc.getDocumentElement(); - - if (childNode == null) { - log.info("Failed to parse XMLObject from '" + reference + "'."); - throw new SLCommandException(4111); - } - - XMLObject xmlObject = createXMLObject(childNode); - - setXMLObjectAndReferenceXML(xmlObject, transforms); - - } else { - - // Include content Base64 encoded. - XMLObject xmlObject = createXMLObject(streamData.getStream()); - - setXMLObjectAndReferenceBase64(xmlObject, transforms); - - } - - } - - /** - * Configures this DataObject as an detached DataObject with the information - * provided in the given sl:DataObject and optionally - * transforms. - * - * @param dataObject - * the sl:DataObject - * @param transforms - * an optional Transforms object, may be null - * - * @throws SLCommandException - * if configuring this DataObject with the information provided in - * the sl:DataObject fails. - * @throws SLRequestException - * if the information provided in the sl:DataObject - * does not conform to the security layer specification. - * @throws NullPointerException - * if dataObject is null - */ - private void setDetachedDataObject( - Base64XMLLocRefOptRefContentType dataObject, XSECTTransforms transforms) - throws SLCommandException, SLRequestException { - - String referenceURI = dataObject.getReference(); - - if (referenceURI == null) { - - // not allowed - log.info("XML structure of the command request contains an " + - "invalid combination of optional elements or attributes. " + - "DataObject of structure='detached' must contain a reference."); - throw new SLRequestException(3003); - - } else { - - DigestMethod dm; - try { - dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx); - } catch (NoSuchAlgorithmException e) { - log.error("Failed to get DigestMethod.", e); - throw new SLCommandException(4006); - } catch (InvalidAlgorithmParameterException e) { - log.error("Failed to get DigestMethod.", e); - throw new SLCommandException(4006); - } - - String idValue = ctx.getIdValueFactory().createIdValue("Reference"); - - reference = new XSECTReference(referenceURI, dm, transforms, null, idValue); - - // case D: - // - // The Reference attribute contains a URI that is used by the Citizen Card - // Environment to code the reference to the data object as part of the XML - // signature (attribute URI in the dsig:Reference) element. The content of - // sl:DataObject represents the data object. - - if (dataObject.getLocRefContent() != null) { - String locRef = dataObject.getLocRefContent(); - try { - this.reference.setDereferencer(new LocRefDereferencer(ctx.getDereferencerContext(), locRef)); - } catch (URISyntaxException e) { - log.info("Invalid URI '" + locRef + "' in DataObject.", e); - throw new SLCommandException(4003); - } catch (IllegalArgumentException e) { - log.info("LocRef URI of '" + locRef + "' not supported in DataObject. ", e); - throw new SLCommandException(4003); - } - } else if (dataObject.getBase64Content() != null) { - byte[] base64Content = dataObject.getBase64Content(); - this.reference.setDereferencer(new ByteArrayDereferencer(base64Content)); - } else if (dataObject.getXMLContent() != null) { - XMLContentType xmlContent = (XMLContentType) dataObject.getXMLContent(); - byte[] bytes = xmlContent.getRedirectedStream().toByteArray(); - this.reference.setDereferencer(new ByteArrayDereferencer(bytes)); - } else { - - // case C: - // - // The Reference attribute contains a URI that must be resolved by the - // Citizen Card Environment to obtain the data object. The Reference - // attribute contains a URI that is used by the Citizen Card Environment - // to code the reference to the data object as part of the XML signature - // (attribute URI in the dsig:Reference) element. The content of - // sl:DataObject remains empty. - - } - - } - } - - /** - * Returns the preferred sl:TransformInfo from the given list of - * transformInfos, or null if none of the given - * transformInfos is preferred over the others. - * - * @param transformsInfos - * a list of sl:TransformInfos - * - * @return the selected sl:TransformInfo or null, if - * none is preferred over the others - */ - private TransformsInfoType selectPreferredTransformsInfo(List transformsInfos) { - - Map mimeTypes = new HashMap(); - - StringBuilder debugString = null; - if (log.isDebugEnabled()) { - debugString = new StringBuilder(); - debugString.append("Got " + transformsInfos.size() + " TransformsInfo(s):"); - } - - for (TransformsInfoType transformsInfoType : transformsInfos) { - MetaInfoType finalDataMetaInfo = transformsInfoType.getFinalDataMetaInfo(); - String mimeType = finalDataMetaInfo.getMimeType(); - String description = finalDataMetaInfo.getDescription(); - mimeTypes.put(mimeType, transformsInfoType); - if (debugString != null) { - debugString.append("\n FinalDataMetaInfo: MIME-Type="); - debugString.append(mimeType); - if (description != null) { - debugString.append(" "); - debugString.append(description); - } - } - } - - if (debugString != null) { - log.debug(debugString); - } - - // look for preferred transform - for (String mimeType : DEFAULT_PREFFERED_MIME_TYPES) { - if (mimeTypes.containsKey(mimeType)) { - return mimeTypes.get(mimeType); - } - } - - // no preferred transform - return null; - - } - - /** - * Create an instance of ds:Transforms from the given - * sl:TransformsInfo. - * - * @param transformsInfo - * the sl:TransformsInfo - * - * @return a corresponding unmarshalled ds:Transforms, or - * null if the given sl:TransformsInfo does - * not contain a dsig:Transforms element - * - * @throws SLRequestException - * if the ds:Transforms in the given - * transformsInfo are not valid or cannot be parsed. - * - * @throws MarshalException - * if the ds:Transforms in the given - * transformsInfo cannot be unmarshalled. - */ - private XSECTTransforms createTransforms(TransformsInfoType transformsInfo) throws SLRequestException, MarshalException { - - ByteArrayOutputStream redirectedStream = ((at.gv.egiz.slbinding.impl.TransformsInfoType) transformsInfo).getRedirectedStream(); - byte[] transformBytes = (redirectedStream != null) ? redirectedStream.toByteArray() : null; - - if (transformBytes != null && transformBytes.length > 0) { - - // debug - if (log.isTraceEnabled()) { - StringBuilder sb = new StringBuilder(); - sb.append("Trying to parse transforms:\n"); - sb.append(new String(transformBytes, Charset.forName("UTF-8"))); - log.trace(sb); - } - - DOMImplementationLS domImplLS = DOMUtils.getDOMImplementationLS(); - LSInput input = domImplLS.createLSInput(); - input.setByteStream(new ByteArrayInputStream(transformBytes)); - - LSParser parser = domImplLS.createLSParser( - DOMImplementationLS.MODE_SYNCHRONOUS, null); - DOMConfiguration domConfig = parser.getDomConfig(); - SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler(); - domConfig.setParameter("error-handler", errorHandler); - domConfig.setParameter("validate", Boolean.FALSE); - - Document document; - try { - document = parser.parse(input); - } catch (DOMException e) { - log.info("Failed to parse dsig:Transforms.", e); - throw new SLRequestException(3002); - } catch (LSException e) { - log.info("Failed to parse dsig:Transforms.", e); - throw new SLRequestException(3002); - } - - // adopt ds:Transforms - Element documentElement = document.getDocumentElement(); - Node adoptedTransforms = ctx.getDocument().adoptNode(documentElement); - - DOMCryptoContext context = new DOMCryptoContext(); - - // unmarshall ds:Transforms - return new XSECTTransforms(context, adoptedTransforms); - - } else { - return null; - } - - } - - /** - * Sets the mimeType and the description value - * for this DataObject. - * - * @param metaInfoType the sl:FinalMetaDataInfo - * - * @throws NullPointerException if metaInfoType is null - */ - private void setFinalDataMetaInfo(MetaInfoType metaInfoType) { - - this.mimeType = metaInfoType.getMimeType(); - this.description = metaInfoType.getDescription(); - - } - - /** - * Selects an appropriate transformation path (if present) from the given list - * of sl:TransformInfos, sets the corresponding final data meta info and - * returns the corresponding unmarshalled ds:Transforms. - * - * @param transformsInfos the sl:TransformInfos - * - * @return the unmarshalled ds:Transforms, or null if - * no transformation path has been selected. - * - * @throws SLRequestException if the given list ds:TransformsInfo contains - * an invalid ds:Transforms element, or no suitable transformation path - * can be found. - */ - private XSECTTransforms createTransformsAndSetFinalDataMetaInfo( - List transformsInfos) throws SLRequestException { - - TransformsInfoType preferredTransformsInfo = selectPreferredTransformsInfo(transformsInfos); - // try preferred transform - if (preferredTransformsInfo != null) { - - try { - XSECTTransforms transforms = createTransforms(preferredTransformsInfo); - setFinalDataMetaInfo(preferredTransformsInfo.getFinalDataMetaInfo()); - return transforms; - } catch (MarshalException e) { - - String mimeType = preferredTransformsInfo.getFinalDataMetaInfo().getMimeType(); - log.info("Failed to unmarshal preferred transformation path (MIME-Type=" - + mimeType + ").", e); - - } - - } - - // look for another suitable transformation path - for (TransformsInfoType transformsInfoType : transformsInfos) { - - try { - XSECTTransforms transforms = createTransforms(transformsInfoType); - setFinalDataMetaInfo(transformsInfoType.getFinalDataMetaInfo()); - return transforms; - } catch (MarshalException e) { - - String mimeType = transformsInfoType.getFinalDataMetaInfo().getMimeType(); - log.info("Failed to unmarshal transformation path (MIME-Type=" - + mimeType + ").", e); - } - - } - - // no suitable transformation path found - throw new SLRequestException(3003); - - } - - /** - * Create an XMLObject with the Base64 encoding of the given - * content. - * - * @param content - * the to-be Base64 encoded content - * @return an XMLObject with the Base64 encoded content - */ - private XMLObject createXMLObject(InputStream content) { - - Text textNode; - try { - textNode = at.gv.egiz.dom.DOMUtils.createBase64Text(content, ctx.getDocument()); - } catch (IOException e) { - log.error(e); - throw new SLRuntimeException(e); - } - - DOMStructure structure = new DOMStructure(textNode); - - String idValue = ctx.getIdValueFactory().createIdValue("Object"); - - return ctx.getSignatureFactory().newXMLObject(Collections.singletonList(structure), idValue, null, null); - - } - - /** - * Create an XMLObject with the given content node. - * - * @param content the content node - * - * @return an XMLObject with the given content - */ - private XMLObject createXMLObject(Node content) { - - String idValue = ctx.getIdValueFactory().createIdValue("Object"); - - List structures = Collections.singletonList(new DOMStructure(content)); - - return ctx.getSignatureFactory().newXMLObject(structures, idValue, null, null); - - } - - /** - * Sets the given xmlObject and creates and sets a corresponding - * Reference. - *

- * A transform to Base64-decode the xmlObject's content is inserted at the top - * of to the optional transforms if given, or to a newly created - * Transforms element if transforms is - * null. - * - * @param xmlObject - * the XMLObject - * @param transforms - * an optional Transforms element (may be - * null) - * - * @throws SLCommandException - * if creating the Reference fails - * @throws NullPointerException - * if xmlObject is null - */ - private void setXMLObjectAndReferenceBase64(XMLObject xmlObject, XSECTTransforms transforms) throws SLCommandException { - - // create reference URI - // - // NOTE: the ds:Object can be referenced directly, as the Base64 transform - // operates on the text() of the input nodelist. - // - String referenceURI = "#" + xmlObject.getId(); - - // create Base64 Transform - Transform transform; - try { - transform = ctx.getSignatureFactory().newTransform(Transform.BASE64, (TransformParameterSpec) null); - } catch (NoSuchAlgorithmException e) { - // algorithm must be present - throw new SLRuntimeException(e); - } catch (InvalidAlgorithmParameterException e) { - // algorithm does not take parameters - throw new SLRuntimeException(e); - } - - if (transforms == null) { - transforms = new XSECTTransforms(Collections.singletonList(transform)); - } else { - transforms.insertTransform(transform); - } - - DigestMethod dm; - try { - dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx); - } catch (NoSuchAlgorithmException e) { - log.error("Failed to get DigestMethod.", e); - throw new SLCommandException(4006); - } catch (InvalidAlgorithmParameterException e) { - log.error("Failed to get DigestMethod.", e); - throw new SLCommandException(4006); - } - String id = ctx.getIdValueFactory().createIdValue("Reference"); - - this.xmlObject = xmlObject; - this.reference = new XSECTReference(referenceURI, dm, transforms, null, id); - - } - - /** - * Sets the given xmlObject and creates and sets a corresponding - * Reference. - *

- * A transform to select the xmlObject's content is inserted at the top of to - * the optional transforms if given, or to a newly created - * Transforms element if transforms is - * null. - *

- * - * @param xmlObject - * the XMLObject - * @param transforms - * an optional Transforms element (may be - * null) - * - * @throws SLCommandException - * if creating the Reference fails - * @throws NullPointerException - * if xmlObject is null - */ - private void setXMLObjectAndReferenceXML(XMLObject xmlObject, XSECTTransforms transforms) throws SLCommandException { - - // create reference URI - String referenceURI = "#" + xmlObject.getId(); - - // create Transform to select ds:Object's children - Transform xpathTransform; - Transform c14nTransform; - try { - - XPathType xpath = new XPathType("id(\"" + xmlObject.getId() + "\")/node()", XPathType.Filter.INTERSECT); - List xpaths = Collections.singletonList(xpath); - XPathFilter2ParameterSpec params = new XPathFilter2ParameterSpec(xpaths); - - xpathTransform = ctx.getSignatureFactory().newTransform(Transform.XPATH2, params); - - // add exclusive canonicalization to avoid signing the namespace context of the ds:Object - c14nTransform = ctx.getSignatureFactory().newTransform(CanonicalizationMethod.EXCLUSIVE, (TransformParameterSpec) null); - - } catch (NoSuchAlgorithmException e) { - // algorithm must be present - throw new SLRuntimeException(e); - } catch (InvalidAlgorithmParameterException e) { - // params must be appropriate - throw new SLRuntimeException(e); - } - - if (transforms == null) { - List newTransfroms = new ArrayList(); - newTransfroms.add(xpathTransform); - newTransfroms.add(c14nTransform); - transforms = new XSECTTransforms(newTransfroms); - } else { - transforms.insertTransform(xpathTransform); - } - - DigestMethod dm; - try { - dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx); - } catch (NoSuchAlgorithmException e) { - log.error("Failed to get DigestMethod.", e); - throw new SLCommandException(4006); - } catch (InvalidAlgorithmParameterException e) { - log.error("Failed to get DigestMethod.", e); - throw new SLCommandException(4006); - } - String id = ctx.getIdValueFactory().createIdValue("Reference"); - - this.xmlObject = xmlObject; - this.reference = new XSECTReference(referenceURI, dm, transforms, null, id); - - } - - /** - * Parses the given xmlContent and returns a corresponding - * document fragment. - * - *

- * The to-be parsed content is surrounded by ... elements to - * allow for mixed (e.g. Text and Element) content in XMLContent. - *

- * - * @param xmlContent - * the XMLContent to-be parsed - * - * @return a document fragment containing the parsed nodes - * - * @throws SLCommandException - * if parsing the given xmlContent fails - * - * @throws NullPointerException - * if xmlContent is null - */ - private DocumentFragment parseDataObject(XMLContentType xmlContent) throws SLCommandException { - - ByteArrayOutputStream redirectedStream = xmlContent.getRedirectedStream(); - - // Note: We can assume a fixed character encoding of UTF-8 for the - // content of the redirect stream as the content has already been parsed - // and serialized again to the redirect stream. - - List inputStreams = new ArrayList(); - try { - // dummy start element - inputStreams.add(new ByteArrayInputStream("".getBytes("UTF-8"))); - - // content - inputStreams.add(new ByteArrayInputStream(redirectedStream.toByteArray())); - - // dummy end element - inputStreams.add(new ByteArrayInputStream("".getBytes("UTF-8"))); - } catch (UnsupportedEncodingException e) { - throw new SLRuntimeException(e); - } - - SequenceInputStream inputStream = new SequenceInputStream(Collections.enumeration(inputStreams)); - - // parse DataObject - Document doc = parseDataObject(inputStream, "UTF-8"); - - Element documentElement = doc.getDocumentElement(); - - if (documentElement == null || - !"dummy".equals(documentElement.getLocalName())) { - log.info("Failed to parse DataObject XMLContent."); - throw new SLCommandException(4111); - } - - DocumentFragment fragment = doc.createDocumentFragment(); - while (documentElement.getFirstChild() != null) { - fragment.appendChild(documentElement.getFirstChild()); - } - - // log parsed document - if (log.isTraceEnabled()) { - - StringWriter writer = new StringWriter(); - - writer.write("DataObject:\n"); - - LSOutput output = domImplLS.createLSOutput(); - output.setCharacterStream(writer); - output.setEncoding("UTF-8"); - LSSerializer serializer = domImplLS.createLSSerializer(); - serializer.getDomConfig().setParameter("xml-declaration", Boolean.FALSE); - serializer.write(fragment, output); - - log.trace(writer.toString()); - } - - return fragment; - - } - - /** - * Parses the given inputStream using the given - * encoding and returns the parsed document. - * - * @param inputStream - * the to-be parsed input - * - * @param encoding - * the encoding to be used for parsing the given - * inputStream - * - * @return the parsed document - * - * @throws SLCommandException - * if parsing the inputStream fails. - * - * @throws NullPointerException - * if inputStram is null - */ - private Document parseDataObject(InputStream inputStream, String encoding) throws SLCommandException { - - LSInput input = domImplLS.createLSInput(); - input.setByteStream(inputStream); - - if (encoding != null) { - input.setEncoding(encoding); - } - - LSParser parser = domImplLS.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null); - DOMConfiguration domConfig = parser.getDomConfig(); - SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler(); - domConfig.setParameter("error-handler", errorHandler); - domConfig.setParameter("validate", Boolean.FALSE); - - Document doc; - try { - doc = parser.parse(input); - } catch (DOMException e) { - log.info("Existing XML document cannot be parsed.", e); - throw new SLCommandException(4111); - } catch (LSException e) { - log.info("Existing XML document cannot be parsed. ", e); - throw new SLCommandException(4111); - } - - if (errorHandler.hasErrors()) { - // log errors - if (log.isInfoEnabled()) { - List errorMessages = errorHandler.getErrorMessages(); - StringBuffer sb = new StringBuffer(); - for (String errorMessage : errorMessages) { - sb.append(" "); - sb.append(errorMessage); - } - log.info("Existing XML document cannot be parsed. " + sb.toString()); - } - throw new SLCommandException(4111); - } - - return doc; - - } - - -} + + /** + * Configures this DataObject with the information provided within the given + * sl:DataObjectInfo. + * + * @param dataObjectInfo + * the sl:DataObjectInfo + * + * @throws SLCommandException + * if configuring this DataObject with the information provided in + * the sl:DataObjectInfo fails. + * @throws SLRequestException + * if the information provided in the sl:DataObjectInfo + * does not conform to the security layer specification. + * @throws NullPointerException + * if dataObjectInfo is null + */ + public void setDataObjectInfo(DataObjectInfoType dataObjectInfo) throws SLCommandException, SLRequestException { + + Base64XMLLocRefOptRefContentType dataObject = dataObjectInfo.getDataObject(); + String structure = dataObjectInfo.getStructure(); + + // select and unmarshal an appropriate transformation path if provided + // and set the final data meta information + XSECTTransforms transforms = createTransformsAndSetFinalDataMetaInfo(dataObjectInfo.getTransformsInfo()); + + if ("enveloping".equals(structure)) { + + // configure this DataObject as an enveloped DataObject + setEnvelopedDataObject(dataObject, transforms); + + } else if ("detached".equals(structure)) { + + // configure this DataObject as an detached DataObject + setDetachedDataObject(dataObject, transforms); + + } + // other values are not allowed by the schema and are therefore ignored + + } + + private byte[] getTransformsBytes(at.gv.egiz.slbinding.impl.TransformsInfoType ti) { + return ti.getRedirectedStream().toByteArray(); +// byte[] transformsBytes = ti.getRedirectedStream().toByteArray(); +// +// if (transformsBytes == null || transformsBytes.length == 0) { +// return null; +// } +// +// String dsigPrefix = ti.getNamespaceContext().getNamespaceURI("http://www.w3.org/2000/09/xmldsig#"); +// byte[] pre, post; +// if (dsigPrefix == null) { +// log.trace("XMLDSig not declared in outside dsig:Transforms"); +// pre = "".getBytes(); +// post = "".getBytes(); +// } else { +// log.trace("XMLDSig bound to prefix " + dsigPrefix); +// pre = ("").getBytes(); +// post = "".getBytes(); +// } +// +// byte[] workaround = new byte[pre.length + transformsBytes.length + post.length]; +// System.arraycopy(pre, 0, workaround, 0, pre.length); +// System.arraycopy(transformsBytes, 0, workaround, pre.length, transformsBytes.length); +// System.arraycopy(post, 0, workaround, pre.length + transformsBytes.length, post.length); +// return workaround; + } + + /** + * Configures this DataObject as an enveloped DataObject with the information + * provided within the given sl:DataObject. + * + * @param dataObject + * the sl:DataObject + * @param transforms + * an optional Transforms element (may be + * null) + * + * @throws SLCommandException + * if configuring this DataObject with the information provided in + * the sl:DataObject fails. + * @throws SLRequestException + * if the information provided in the sl:DataObject + * does not conform to the security layer specification. + * @throws NullPointerException + * if dataObject is null + */ + private void setEnvelopedDataObject( + Base64XMLLocRefOptRefContentType dataObject, XSECTTransforms transforms) + throws SLCommandException, SLRequestException { + + String reference = dataObject.getReference(); + if (reference == null) { + // + // case A + // + // The Reference attribute is not used; the content of sl:DataObject represents the data object. + // If the data object is XML-coded (the sl:XMLContent element is used in sl:DataObject), then it + // must be incorporated in the signature structure as parsed XML. + // + + if (dataObject.getBase64Content() != null) { + + log.debug("Adding DataObject (Base64Content) without a reference URI."); + + // create XMLObject + XMLObject xmlObject = createXMLObject(new ByteArrayInputStream(dataObject.getBase64Content())); + + setXMLObjectAndReferenceBase64(xmlObject, transforms); + + } else if (dataObject.getXMLContent() != null) { + + log.debug("Adding DataObject (XMLContent) without a reference URI."); + + // create XMLObject + DocumentFragment content = parseDataObject((XMLContentType) dataObject.getXMLContent()); + XMLObject xmlObject = createXMLObject(content); + + setXMLObjectAndReferenceXML(xmlObject, transforms); + + } else if (dataObject.getLocRefContent() != null) { + + log.debug("Adding DataObject (LocRefContent) without a reference URI."); + + setEnvelopedDataObject(dataObject.getLocRefContent(), transforms); + + } else { + + // not allowed + log.info("XML structure of the command request contains an " + + "invalid combination of optional elements or attributes. " + + "DataObject of structure='enveloped' without a reference must contain content."); + throw new SLRequestException(3003); + + } + + } else { + + if (dataObject.getBase64Content() == null && + dataObject.getXMLContent() == null && + dataObject.getLocRefContent() == null) { + + // + // case B + // + // The Reference attribute contains a URI that must be resolved by the + // Citizen Card Environment to obtain the data object. + // The content of sl:DataObject remains empty + // + + log.debug("Adding DataObject from reference URI '" + reference + "'."); + + setEnvelopedDataObject(reference, transforms); + + } else { + + // not allowed + log.info("XML structure of the command request contains an " + + "invalid combination of optional elements or attributes. " + + "DataObject of structure='enveloped' with reference must not contain content."); + throw new SLRequestException(3003); + + } + + + } + + } + + /** + * Configures this DataObject as an enveloped DataObject with the content to + * be dereferenced from the given reference. + * + * @param reference + * the reference URI + * @param transforms + * an optional Transforms element (may be + * null) + * + * @throws SLCommandException + * if dereferencing the given reference fails, or if + * configuring this DataObject with the data dereferenced from the + * given reference fails. + * @throws NullPointerException + * if reference is null + */ + private void setEnvelopedDataObject(String reference, XSECTTransforms transforms) throws SLCommandException { + + if (reference == null) { + throw new NullPointerException("Argument 'reference' must not be null."); + } + + // dereference URL + URLDereferencer dereferencer = URLDereferencer.getInstance(); + + StreamData streamData; + try { + streamData = dereferencer.dereference(reference, ctx.getDereferencerContext()); + } catch (IOException e) { + log.info("Failed to dereference XMLObject from '" + reference + "'.", e); + throw new SLCommandException(4110); + } + + Node childNode; + + String contentType = streamData.getContentType(); + if (contentType.startsWith("text/xml")) { + + // If content type is text/xml parse content. + String charset = HttpUtil.getCharset(contentType, true); + + Document doc = parseDataObject(streamData.getStream(), charset); + + childNode = doc.getDocumentElement(); + + if (childNode == null) { + log.info("Failed to parse XMLObject from '" + reference + "'."); + throw new SLCommandException(4111); + } + + XMLObject xmlObject = createXMLObject(childNode); + + setXMLObjectAndReferenceXML(xmlObject, transforms); + + } else { + + // Include content Base64 encoded. + XMLObject xmlObject = createXMLObject(streamData.getStream()); + + setXMLObjectAndReferenceBase64(xmlObject, transforms); + + } + + } + + /** + * Configures this DataObject as an detached DataObject with the information + * provided in the given sl:DataObject and optionally + * transforms. + * + * @param dataObject + * the sl:DataObject + * @param transforms + * an optional Transforms object, may be null + * + * @throws SLCommandException + * if configuring this DataObject with the information provided in + * the sl:DataObject fails. + * @throws SLRequestException + * if the information provided in the sl:DataObject + * does not conform to the security layer specification. + * @throws NullPointerException + * if dataObject is null + */ + private void setDetachedDataObject( + Base64XMLLocRefOptRefContentType dataObject, XSECTTransforms transforms) + throws SLCommandException, SLRequestException { + + String referenceURI = dataObject.getReference(); + + if (referenceURI == null) { + + // not allowed + log.info("XML structure of the command request contains an " + + "invalid combination of optional elements or attributes. " + + "DataObject of structure='detached' must contain a reference."); + throw new SLRequestException(3003); + + } else { + + DigestMethod dm; + try { + dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx); + } catch (NoSuchAlgorithmException e) { + log.error("Failed to get DigestMethod.", e); + throw new SLCommandException(4006); + } catch (InvalidAlgorithmParameterException e) { + log.error("Failed to get DigestMethod.", e); + throw new SLCommandException(4006); + } + + String idValue = ctx.getIdValueFactory().createIdValue("Reference"); + + reference = new XSECTReference(referenceURI, dm, transforms, null, idValue); + + // case D: + // + // The Reference attribute contains a URI that is used by the Citizen Card + // Environment to code the reference to the data object as part of the XML + // signature (attribute URI in the dsig:Reference) element. The content of + // sl:DataObject represents the data object. + + if (dataObject.getLocRefContent() != null) { + String locRef = dataObject.getLocRefContent(); + try { + this.reference.setDereferencer(new LocRefDereferencer(ctx.getDereferencerContext(), locRef)); + } catch (URISyntaxException e) { + log.info("Invalid URI '" + locRef + "' in DataObject.", e); + throw new SLCommandException(4003); + } catch (IllegalArgumentException e) { + log.info("LocRef URI of '" + locRef + "' not supported in DataObject. ", e); + throw new SLCommandException(4003); + } + } else if (dataObject.getBase64Content() != null) { + byte[] base64Content = dataObject.getBase64Content(); + this.reference.setDereferencer(new ByteArrayDereferencer(base64Content)); + } else if (dataObject.getXMLContent() != null) { + XMLContentType xmlContent = (XMLContentType) dataObject.getXMLContent(); + byte[] bytes = xmlContent.getRedirectedStream().toByteArray(); + this.reference.setDereferencer(new ByteArrayDereferencer(bytes)); + } else { + + // case C: + // + // The Reference attribute contains a URI that must be resolved by the + // Citizen Card Environment to obtain the data object. The Reference + // attribute contains a URI that is used by the Citizen Card Environment + // to code the reference to the data object as part of the XML signature + // (attribute URI in the dsig:Reference) element. The content of + // sl:DataObject remains empty. + + } + + } + } + + /** + * Returns the preferred sl:TransformInfo from the given list of + * transformInfos, or null if none of the given + * transformInfos is preferred over the others. + * + * @param transformsInfos + * a list of sl:TransformInfos + * + * @return the selected sl:TransformInfo or null, if + * none is preferred over the others + */ + private TransformsInfoType selectPreferredTransformsInfo(List transformsInfos) { + + Map mimeTypes = new HashMap(); + + StringBuilder debugString = null; + if (log.isDebugEnabled()) { + debugString = new StringBuilder(); + debugString.append("Got " + transformsInfos.size() + " TransformsInfo(s):"); + } + + for (TransformsInfoType transformsInfoType : transformsInfos) { + MetaInfoType finalDataMetaInfo = transformsInfoType.getFinalDataMetaInfo(); + String mimeType = finalDataMetaInfo.getMimeType(); + String description = finalDataMetaInfo.getDescription(); + mimeTypes.put(mimeType, transformsInfoType); + if (debugString != null) { + debugString.append("\n FinalDataMetaInfo: MIME-Type="); + debugString.append(mimeType); + if (description != null) { + debugString.append(" "); + debugString.append(description); + } + } + } + + if (debugString != null) { + log.debug(debugString); + } + + // look for preferred transform + for (String mimeType : DEFAULT_PREFFERED_MIME_TYPES) { + if (mimeTypes.containsKey(mimeType)) { + return mimeTypes.get(mimeType); + } + } + + // no preferred transform + return null; + + } + + /** + * Create an instance of ds:Transforms from the given + * sl:TransformsInfo. + * + * @param transformsInfo + * the sl:TransformsInfo + * + * @return a corresponding unmarshalled ds:Transforms, or + * null if the given sl:TransformsInfo does + * not contain a dsig:Transforms element + * + * @throws SLRequestException + * if the ds:Transforms in the given + * transformsInfo are not valid or cannot be parsed. + * + * @throws MarshalException + * if the ds:Transforms in the given + * transformsInfo cannot be unmarshalled. + */ + private XSECTTransforms createTransforms(TransformsInfoType transformsInfo) throws SLRequestException, MarshalException { + + byte[] transforms = getTransformsBytes((at.gv.egiz.slbinding.impl.TransformsInfoType) transformsInfo); + + if (transforms != null && transforms.length > 0) { + // debug + if (log.isTraceEnabled()) { + StringBuilder sb = new StringBuilder(); + sb.append("Trying to parse transforms:\n"); + sb.append(new String(transforms, Charset.forName("UTF-8"))); + log.trace(sb); + } + + DOMImplementationLS domImplLS = DOMUtils.getDOMImplementationLS(); + LSInput input = domImplLS.createLSInput(); + input.setByteStream(new ByteArrayInputStream(transforms)); + + LSParser parser = domImplLS.createLSParser( + DOMImplementationLS.MODE_SYNCHRONOUS, null); + DOMConfiguration domConfig = parser.getDomConfig(); + SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler(); + domConfig.setParameter("error-handler", errorHandler); + domConfig.setParameter("validate", Boolean.FALSE); + + Document document; + try { + document = parser.parse(input); + } catch (DOMException e) { + log.info("Failed to parse dsig:Transforms.", e); + throw new SLRequestException(3002); + } catch (LSException e) { + log.info("Failed to parse dsig:Transforms.", e); + throw new SLRequestException(3002); + } + + // adopt ds:Transforms + Element transformsElt = document.getDocumentElement(); + Node adoptedTransforms = ctx.getDocument().adoptNode(transformsElt); + + DOMCryptoContext context = new DOMCryptoContext(); + + // unmarshall ds:Transforms + return new XSECTTransforms(context, adoptedTransforms); + + } else { + return null; + } + + +// TransformsType transformsType = transformsInfo.getTransforms(); +// if (transformsType == null) { +// return null; +// } +// List transformList = transformsType.getTransform(); +// +// DOMImplementationLS domImplLS = DOMUtils.getDOMImplementationLS(); +//// Document transformsDoc = ((DOMImplementation) domImplLS).createDocument("http://www.w3.org/2000/09/xmldsig#", "Transforms", null); +//// Element transforms = transformsDoc.getDocumentElement(); +// Document transformsDoc = DOMUtils.createDocument(); +// Element transforms = transformsDoc.createElementNS( +// "http://www.w3.org/2000/09/xmldsig#", +// Signature.XMLDSIG_PREFIX + ":Transforms"); +// transformsDoc.appendChild(transforms); +// +// for (TransformType transformType : transformList) { +// log.trace("found " + transformType.getClass().getName()); +// Element transform = transformsDoc.createElementNS( +// "http://www.w3.org/2000/09/xmldsig#", +// Signature.XMLDSIG_PREFIX + ":Transform"); +// String algorithm = transformType.getAlgorithm(); +// if (algorithm != null) { +// log.trace("found algorithm " + algorithm); +// transform.setAttribute("Algorithm", algorithm); +// } +// +// at.gv.egiz.slbinding.impl.TransformType t = (at.gv.egiz.slbinding.impl.TransformType) transformType; +// byte[] redirectedBytes = t.getRedirectedStream().toByteArray(); +// if (redirectedBytes != null && redirectedBytes.length > 0) { +// if (log.isTraceEnabled()) { +// StringBuilder sb = new StringBuilder(); +// sb.append("Trying to parse dsig:Transform:\n"); +// sb.append(new String(redirectedBytes, Charset.forName("UTF-8"))); +// log.trace(sb); +// } +// LSInput input = domImplLS.createLSInput(); +// input.setByteStream(new ByteArrayInputStream(redirectedBytes)); +// +// LSParser parser = domImplLS.createLSParser( +// DOMImplementationLS.MODE_SYNCHRONOUS, null); +// DOMConfiguration domConfig = parser.getDomConfig(); +// SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler(); +// domConfig.setParameter("error-handler", errorHandler); +// domConfig.setParameter("validate", Boolean.FALSE); +// +// try { +// Document redirectedDoc = parser.parse(input); +// Node redirected = transformsDoc.adoptNode(redirectedDoc.getDocumentElement()); +// transform.appendChild(redirected); +// +// //not supported by Xerces2.9.1 +//// Node redirected = parser.parseWithContext(input, transform, LSParser.ACTION_APPEND_AS_CHILDREN); +// +// } catch (DOMException e) { +// log.info("Failed to parse dsig:Transform.", e); +// throw new SLRequestException(3002); +// } catch (LSException e) { +// log.info("Failed to parse dsig:Transform.", e); +// throw new SLRequestException(3002); +// } +// } +// transforms.appendChild(transform); +// } +// +// //adopt ds:Transforms +// Node adoptedTransforms = ctx.getDocument().adoptNode(transforms); +// DOMCryptoContext context = new DOMCryptoContext(); +// +// // unmarshall ds:Transforms +// return new XSECTTransforms(context, adoptedTransforms); + + } + + /** + * Sets the mimeType and the description value + * for this DataObject. + * + * @param metaInfoType the sl:FinalMetaDataInfo + * + * @throws NullPointerException if metaInfoType is null + */ + private void setFinalDataMetaInfo(MetaInfoType metaInfoType) { + + this.mimeType = metaInfoType.getMimeType(); + this.description = metaInfoType.getDescription(); + + } + + /** + * Selects an appropriate transformation path (if present) from the given list + * of sl:TransformInfos, sets the corresponding final data meta info and + * returns the corresponding unmarshalled ds:Transforms. + * + * @param transformsInfos the sl:TransformInfos + * + * @return the unmarshalled ds:Transforms, or null if + * no transformation path has been selected. + * + * @throws SLRequestException if the given list ds:TransformsInfo contains + * an invalid ds:Transforms element, or no suitable transformation path + * can be found. + */ + private XSECTTransforms createTransformsAndSetFinalDataMetaInfo( + List transformsInfos) throws SLRequestException { + + TransformsInfoType preferredTransformsInfo = selectPreferredTransformsInfo(transformsInfos); + // try preferred transform + if (preferredTransformsInfo != null) { + + try { + XSECTTransforms transforms = createTransforms(preferredTransformsInfo); + setFinalDataMetaInfo(preferredTransformsInfo.getFinalDataMetaInfo()); + return transforms; + } catch (MarshalException e) { + + String mimeType = preferredTransformsInfo.getFinalDataMetaInfo().getMimeType(); + log.info("Failed to unmarshal preferred transformation path (MIME-Type=" + + mimeType + ").", e); + + } + + } + + // look for another suitable transformation path + for (TransformsInfoType transformsInfoType : transformsInfos) { + + try { + XSECTTransforms transforms = createTransforms(transformsInfoType); + setFinalDataMetaInfo(transformsInfoType.getFinalDataMetaInfo()); + return transforms; + } catch (MarshalException e) { + + String mimeType = transformsInfoType.getFinalDataMetaInfo().getMimeType(); + log.info("Failed to unmarshal transformation path (MIME-Type=" + + mimeType + ").", e); + } + + } + + // no suitable transformation path found + throw new SLRequestException(3003); + + } + + /** + * Create an XMLObject with the Base64 encoding of the given + * content. + * + * @param content + * the to-be Base64 encoded content + * @return an XMLObject with the Base64 encoded content + */ + private XMLObject createXMLObject(InputStream content) { + + Text textNode; + try { + textNode = at.gv.egiz.dom.DOMUtils.createBase64Text(content, ctx.getDocument()); + } catch (IOException e) { + log.error(e); + throw new SLRuntimeException(e); + } + + DOMStructure structure = new DOMStructure(textNode); + + String idValue = ctx.getIdValueFactory().createIdValue("Object"); + + return ctx.getSignatureFactory().newXMLObject(Collections.singletonList(structure), idValue, null, null); + + } + + /** + * Create an XMLObject with the given content node. + * + * @param content the content node + * + * @return an XMLObject with the given content + */ + private XMLObject createXMLObject(Node content) { + + String idValue = ctx.getIdValueFactory().createIdValue("Object"); + + List structures = Collections.singletonList(new DOMStructure(content)); + + return ctx.getSignatureFactory().newXMLObject(structures, idValue, null, null); + + } + + /** + * Sets the given xmlObject and creates and sets a corresponding + * Reference. + *

+ * A transform to Base64-decode the xmlObject's content is inserted at the top + * of to the optional transforms if given, or to a newly created + * Transforms element if transforms is + * null. + * + * @param xmlObject + * the XMLObject + * @param transforms + * an optional Transforms element (may be + * null) + * + * @throws SLCommandException + * if creating the Reference fails + * @throws NullPointerException + * if xmlObject is null + */ + private void setXMLObjectAndReferenceBase64(XMLObject xmlObject, XSECTTransforms transforms) throws SLCommandException { + + // create reference URI + // + // NOTE: the ds:Object can be referenced directly, as the Base64 transform + // operates on the text() of the input nodelist. + // + String referenceURI = "#" + xmlObject.getId(); + + // create Base64 Transform + Transform transform; + try { + transform = ctx.getSignatureFactory().newTransform(Transform.BASE64, (TransformParameterSpec) null); + } catch (NoSuchAlgorithmException e) { + // algorithm must be present + throw new SLRuntimeException(e); + } catch (InvalidAlgorithmParameterException e) { + // algorithm does not take parameters + throw new SLRuntimeException(e); + } + + if (transforms == null) { + transforms = new XSECTTransforms(Collections.singletonList(transform)); + } else { + transforms.insertTransform(transform); + } + + DigestMethod dm; + try { + dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx); + } catch (NoSuchAlgorithmException e) { + log.error("Failed to get DigestMethod.", e); + throw new SLCommandException(4006); + } catch (InvalidAlgorithmParameterException e) { + log.error("Failed to get DigestMethod.", e); + throw new SLCommandException(4006); + } + String id = ctx.getIdValueFactory().createIdValue("Reference"); + + this.xmlObject = xmlObject; + this.reference = new XSECTReference(referenceURI, dm, transforms, null, id); + + } + + /** + * Sets the given xmlObject and creates and sets a corresponding + * Reference. + *

+ * A transform to select the xmlObject's content is inserted at the top of to + * the optional transforms if given, or to a newly created + * Transforms element if transforms is + * null. + *

+ * + * @param xmlObject + * the XMLObject + * @param transforms + * an optional Transforms element (may be + * null) + * + * @throws SLCommandException + * if creating the Reference fails + * @throws NullPointerException + * if xmlObject is null + */ + private void setXMLObjectAndReferenceXML(XMLObject xmlObject, XSECTTransforms transforms) throws SLCommandException { + + // create reference URI + String referenceURI = "#" + xmlObject.getId(); + + // create Transform to select ds:Object's children + Transform xpathTransform; + Transform c14nTransform; + try { + + XPathType xpath = new XPathType("id(\"" + xmlObject.getId() + "\")/node()", XPathType.Filter.INTERSECT); + List xpaths = Collections.singletonList(xpath); + XPathFilter2ParameterSpec params = new XPathFilter2ParameterSpec(xpaths); + + xpathTransform = ctx.getSignatureFactory().newTransform(Transform.XPATH2, params); + + // add exclusive canonicalization to avoid signing the namespace context of the ds:Object + c14nTransform = ctx.getSignatureFactory().newTransform(CanonicalizationMethod.EXCLUSIVE, (TransformParameterSpec) null); + + } catch (NoSuchAlgorithmException e) { + // algorithm must be present + throw new SLRuntimeException(e); + } catch (InvalidAlgorithmParameterException e) { + // params must be appropriate + throw new SLRuntimeException(e); + } + + if (transforms == null) { + List newTransfroms = new ArrayList(); + newTransfroms.add(xpathTransform); + newTransfroms.add(c14nTransform); + transforms = new XSECTTransforms(newTransfroms); + } else { + transforms.insertTransform(xpathTransform); + } + + DigestMethod dm; + try { + dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx); + } catch (NoSuchAlgorithmException e) { + log.error("Failed to get DigestMethod.", e); + throw new SLCommandException(4006); + } catch (InvalidAlgorithmParameterException e) { + log.error("Failed to get DigestMethod.", e); + throw new SLCommandException(4006); + } + String id = ctx.getIdValueFactory().createIdValue("Reference"); + + this.xmlObject = xmlObject; + this.reference = new XSECTReference(referenceURI, dm, transforms, null, id); + + } + + /** + * Parses the given xmlContent and returns a corresponding + * document fragment. + * + *

+ * The to-be parsed content is surrounded by ... elements to + * allow for mixed (e.g. Text and Element) content in XMLContent. + *

+ * + * @param xmlContent + * the XMLContent to-be parsed + * + * @return a document fragment containing the parsed nodes + * + * @throws SLCommandException + * if parsing the given xmlContent fails + * + * @throws NullPointerException + * if xmlContent is null + */ + private DocumentFragment parseDataObject(XMLContentType xmlContent) throws SLCommandException { + + ByteArrayOutputStream redirectedStream = xmlContent.getRedirectedStream(); + + // Note: We can assume a fixed character encoding of UTF-8 for the + // content of the redirect stream as the content has already been parsed + // and serialized again to the redirect stream. + + List inputStreams = new ArrayList(); + try { + // dummy start element + inputStreams.add(new ByteArrayInputStream("".getBytes("UTF-8"))); + + // content + inputStreams.add(new ByteArrayInputStream(redirectedStream.toByteArray())); + + // dummy end element + inputStreams.add(new ByteArrayInputStream("".getBytes("UTF-8"))); + } catch (UnsupportedEncodingException e) { + throw new SLRuntimeException(e); + } + + SequenceInputStream inputStream = new SequenceInputStream(Collections.enumeration(inputStreams)); + + // parse DataObject + Document doc = parseDataObject(inputStream, "UTF-8"); + + Element documentElement = doc.getDocumentElement(); + + if (documentElement == null || + !"dummy".equals(documentElement.getLocalName())) { + log.info("Failed to parse DataObject XMLContent."); + throw new SLCommandException(4111); + } + + DocumentFragment fragment = doc.createDocumentFragment(); + while (documentElement.getFirstChild() != null) { + fragment.appendChild(documentElement.getFirstChild()); + } + + // log parsed document + if (log.isTraceEnabled()) { + + StringWriter writer = new StringWriter(); + + writer.write("DataObject:\n"); + + LSOutput output = domImplLS.createLSOutput(); + output.setCharacterStream(writer); + output.setEncoding("UTF-8"); + LSSerializer serializer = domImplLS.createLSSerializer(); + serializer.getDomConfig().setParameter("xml-declaration", Boolean.FALSE); + serializer.write(fragment, output); + + log.trace(writer.toString()); + } + + return fragment; + + } + + /** + * Parses the given inputStream using the given + * encoding and returns the parsed document. + * + * @param inputStream + * the to-be parsed input + * + * @param encoding + * the encoding to be used for parsing the given + * inputStream + * + * @return the parsed document + * + * @throws SLCommandException + * if parsing the inputStream fails. + * + * @throws NullPointerException + * if inputStram is null + */ + private Document parseDataObject(InputStream inputStream, String encoding) throws SLCommandException { + + LSInput input = domImplLS.createLSInput(); + input.setByteStream(inputStream); + + if (encoding != null) { + input.setEncoding(encoding); + } + + LSParser parser = domImplLS.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null); + DOMConfiguration domConfig = parser.getDomConfig(); + SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler(); + domConfig.setParameter("error-handler", errorHandler); + domConfig.setParameter("validate", Boolean.FALSE); + + Document doc; + try { + doc = parser.parse(input); + } catch (DOMException e) { + log.info("Existing XML document cannot be parsed.", e); + throw new SLCommandException(4111); + } catch (LSException e) { + log.info("Existing XML document cannot be parsed. ", e); + throw new SLCommandException(4111); + } + + if (errorHandler.hasErrors()) { + // log errors + if (log.isInfoEnabled()) { + List errorMessages = errorHandler.getErrorMessages(); + StringBuffer sb = new StringBuffer(); + for (String errorMessage : errorMessages) { + sb.append(" "); + sb.append(errorMessage); + } + log.info("Existing XML document cannot be parsed. " + sb.toString()); + } + throw new SLCommandException(4111); + } + + return doc; + + } + + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/Signature.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/Signature.java index 8baa0137..9182e824 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/Signature.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/Signature.java @@ -87,6 +87,8 @@ import at.gv.egiz.bku.utils.urldereferencer.StreamData; import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer; import at.gv.egiz.bku.utils.urldereferencer.URLDereferencerContext; import at.gv.egiz.dom.DOMUtils; +import at.gv.egiz.marshal.NamespacePrefix; +import at.gv.egiz.marshal.NamespacePrefixMapperImpl; import at.gv.egiz.slbinding.impl.XMLContentType; import at.gv.egiz.stal.STAL; import at.gv.egiz.xades.QualifyingPropertiesException; @@ -99,6 +101,7 @@ import at.gv.egiz.xades.QualifyingPropertiesFactory; * @author mcentner */ public class Signature { + public static final String XMLDSIG_PREFIX = "dsig"; /** * Logging facility. @@ -407,7 +410,7 @@ public class Signature { signContext.setProperty("javax.xml.crypto.dsig.cacheReference", Boolean.TRUE); - signContext.putNamespacePrefix(XMLSignature.XMLNS, "dsig"); + signContext.putNamespacePrefix(XMLSignature.XMLNS,XMLDSIG_PREFIX); signContext.setURIDereferencer(new URIDereferncerAdapter(ctx.getDereferencerContext())); diff --git a/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureTest.java b/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureTest.java index 78172dcb..7ce7b42d 100644 --- a/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureTest.java +++ b/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureTest.java @@ -14,185 +14,186 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package at.gv.egiz.bku.slcommands.impl.xsect; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import iaik.xml.crypto.XSecProvider; - -import java.io.IOException; -import java.io.InputStream; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.List; - +package at.gv.egiz.bku.slcommands.impl.xsect; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import iaik.xml.crypto.XSecProvider; + +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.List; + import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSocketFactory; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Unmarshaller; -import javax.xml.crypto.MarshalException; -import javax.xml.crypto.dsig.CanonicalizationMethod; -import javax.xml.crypto.dsig.DigestMethod; -import javax.xml.crypto.dsig.Reference; -import javax.xml.crypto.dsig.SignatureMethod; -import javax.xml.crypto.dsig.Transform; -import javax.xml.crypto.dsig.XMLObject; -import javax.xml.crypto.dsig.XMLSignatureException; -import javax.xml.crypto.dsig.XMLSignatureFactory; -import javax.xml.crypto.dsig.dom.DOMSignContext; -import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; -import javax.xml.crypto.dsig.spec.DigestMethodParameterSpec; -import javax.xml.crypto.dsig.spec.SignatureMethodParameterSpec; -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamException; - -import org.junit.BeforeClass; -import org.junit.Test; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.w3c.dom.ls.DOMImplementationLS; -import org.w3c.dom.ls.LSOutput; -import org.w3c.dom.ls.LSSerializer; - -import at.buergerkarte.namespaces.securitylayer._1.CreateXMLSignatureRequestType; -import at.buergerkarte.namespaces.securitylayer._1.DataObjectInfoType; -import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory; -import at.buergerkarte.namespaces.securitylayer._1.SignatureInfoCreationType; -import at.gv.egiz.bku.slexceptions.SLCommandException; -import at.gv.egiz.bku.slexceptions.SLRequestException; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.crypto.MarshalException; +import javax.xml.crypto.dsig.CanonicalizationMethod; +import javax.xml.crypto.dsig.DigestMethod; +import javax.xml.crypto.dsig.Reference; +import javax.xml.crypto.dsig.SignatureMethod; +import javax.xml.crypto.dsig.Transform; +import javax.xml.crypto.dsig.XMLObject; +import javax.xml.crypto.dsig.XMLSignatureException; +import javax.xml.crypto.dsig.XMLSignatureFactory; +import javax.xml.crypto.dsig.dom.DOMSignContext; +import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; +import javax.xml.crypto.dsig.spec.DigestMethodParameterSpec; +import javax.xml.crypto.dsig.spec.SignatureMethodParameterSpec; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSOutput; +import org.w3c.dom.ls.LSSerializer; + +import at.buergerkarte.namespaces.securitylayer._1.CreateXMLSignatureRequestType; +import at.buergerkarte.namespaces.securitylayer._1.DataObjectInfoType; +import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory; +import at.buergerkarte.namespaces.securitylayer._1.SignatureInfoCreationType; +import at.gv.egiz.bku.slexceptions.SLCommandException; +import at.gv.egiz.bku.slexceptions.SLRequestException; import at.gv.egiz.bku.slexceptions.SLViewerException; -import at.gv.egiz.bku.utils.urldereferencer.StreamData; -import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer; -import at.gv.egiz.bku.utils.urldereferencer.URLDereferencerContext; -import at.gv.egiz.bku.utils.urldereferencer.URLProtocolHandler; -import at.gv.egiz.dom.DOMUtils; -import at.gv.egiz.slbinding.RedirectEventFilter; -import at.gv.egiz.slbinding.RedirectUnmarshallerListener; - -public class SignatureTest { - - private class AlgorithmMethodFactoryImpl implements AlgorithmMethodFactory { - - @Override - public CanonicalizationMethod createCanonicalizationMethod( - SignatureContext signatureContext) { - - XMLSignatureFactory signatureFactory = signatureContext.getSignatureFactory(); - - try { - return signatureFactory.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public DigestMethod createDigestMethod(SignatureContext signatureContext) { - - XMLSignatureFactory signatureFactory = signatureContext.getSignatureFactory(); - - try { - return signatureFactory.newDigestMethod(DigestMethod.SHA1, (DigestMethodParameterSpec) null); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public SignatureMethod createSignatureMethod( - SignatureContext signatureContext) { - - XMLSignatureFactory signatureFactory = signatureContext.getSignatureFactory(); - - try { - return signatureFactory.newSignatureMethod(SignatureMethod.RSA_SHA1, (SignatureMethodParameterSpec) null); - } catch (Exception e) { - throw new RuntimeException(e); - } - - } - - } - - private static final String RESOURCE_PREFIX = "at/gv/egiz/bku/slcommands/impl/"; - - private static Unmarshaller unmarshaller; - - private static PrivateKey privateKey; - - private static X509Certificate certificate; - - @BeforeClass - public static void setUpClass() throws JAXBException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, UnrecoverableKeyException { - - XSecProvider.addAsProvider(true); - - String packageName = ObjectFactory.class.getPackage().getName(); - packageName += ":" - + org.w3._2000._09.xmldsig_.ObjectFactory.class.getPackage().getName(); - JAXBContext jaxbContext = JAXBContext.newInstance(packageName); - - unmarshaller = jaxbContext.createUnmarshaller(); - - initURLDereferencer(); - - ClassLoader classLoader = SignatureTest.class.getClassLoader(); - InputStream certStream = classLoader.getResourceAsStream(RESOURCE_PREFIX + "Cert.p12"); - assertNotNull("Certificate not found.", certStream); - - char[] passwd = "1622".toCharArray(); - - KeyStore keystore = KeyStore.getInstance("PKCS12"); - keystore.load(certStream, passwd); - String firstAlias = keystore.aliases().nextElement(); - certificate = (X509Certificate) keystore.getCertificate(firstAlias); - privateKey = (PrivateKey) keystore.getKey(firstAlias, passwd); - - } - - private static void initURLDereferencer() { - - URLDereferencer.getInstance().registerHandler("testlocal", new URLProtocolHandler() { - - @Override - public StreamData dereference(String url, URLDereferencerContext context) - throws IOException { - - ClassLoader classLoader = SignatureTest.class.getClassLoader(); - - String filename = url.split(":", 2)[1]; - - InputStream stream = classLoader.getResourceAsStream(RESOURCE_PREFIX + filename); - - if (stream == null) { - - throw new IOException("Failed to resolve resource '" + url + "'."); - - } else { - - String contentType; - if (filename.endsWith(".xml")) { - contentType = "text/xml"; - } else if (filename.endsWith(".txt")) { - contentType = "text/plain"; - } else { - contentType = ""; - } - - return new StreamData(url, contentType, stream); - - } - +import at.gv.egiz.bku.utils.urldereferencer.StreamData; +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer; +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencerContext; +import at.gv.egiz.bku.utils.urldereferencer.URLProtocolHandler; +import at.gv.egiz.dom.DOMUtils; +import at.gv.egiz.slbinding.RedirectEventFilter; +import at.gv.egiz.slbinding.RedirectUnmarshallerListener; +import org.junit.Ignore; + +public class SignatureTest { + + private class AlgorithmMethodFactoryImpl implements AlgorithmMethodFactory { + + @Override + public CanonicalizationMethod createCanonicalizationMethod( + SignatureContext signatureContext) { + + XMLSignatureFactory signatureFactory = signatureContext.getSignatureFactory(); + + try { + return signatureFactory.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public DigestMethod createDigestMethod(SignatureContext signatureContext) { + + XMLSignatureFactory signatureFactory = signatureContext.getSignatureFactory(); + + try { + return signatureFactory.newDigestMethod(DigestMethod.SHA1, (DigestMethodParameterSpec) null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public SignatureMethod createSignatureMethod( + SignatureContext signatureContext) { + + XMLSignatureFactory signatureFactory = signatureContext.getSignatureFactory(); + + try { + return signatureFactory.newSignatureMethod(SignatureMethod.RSA_SHA1, (SignatureMethodParameterSpec) null); + } catch (Exception e) { + throw new RuntimeException(e); + } + + } + + } + + private static final String RESOURCE_PREFIX = "at/gv/egiz/bku/slcommands/impl/"; + + private static Unmarshaller unmarshaller; + + private static PrivateKey privateKey; + + private static X509Certificate certificate; + + @BeforeClass + public static void setUpClass() throws JAXBException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, UnrecoverableKeyException { + + XSecProvider.addAsProvider(true); + + String packageName = ObjectFactory.class.getPackage().getName(); + packageName += ":" + + org.w3._2000._09.xmldsig_.ObjectFactory.class.getPackage().getName(); + JAXBContext jaxbContext = JAXBContext.newInstance(packageName); + + unmarshaller = jaxbContext.createUnmarshaller(); + + initURLDereferencer(); + + ClassLoader classLoader = SignatureTest.class.getClassLoader(); + InputStream certStream = classLoader.getResourceAsStream(RESOURCE_PREFIX + "Cert.p12"); + assertNotNull("Certificate not found.", certStream); + + char[] passwd = "1622".toCharArray(); + + KeyStore keystore = KeyStore.getInstance("PKCS12"); + keystore.load(certStream, passwd); + String firstAlias = keystore.aliases().nextElement(); + certificate = (X509Certificate) keystore.getCertificate(firstAlias); + privateKey = (PrivateKey) keystore.getKey(firstAlias, passwd); + + } + + private static void initURLDereferencer() { + + URLDereferencer.getInstance().registerHandler("testlocal", new URLProtocolHandler() { + + @Override + public StreamData dereference(String url, URLDereferencerContext context) + throws IOException { + + ClassLoader classLoader = SignatureTest.class.getClassLoader(); + + String filename = url.split(":", 2)[1]; + + InputStream stream = classLoader.getResourceAsStream(RESOURCE_PREFIX + filename); + + if (stream == null) { + + throw new IOException("Failed to resolve resource '" + url + "'."); + + } else { + + String contentType; + if (filename.endsWith(".xml")) { + contentType = "text/xml"; + } else if (filename.endsWith(".txt")) { + contentType = "text/plain"; + } else { + contentType = ""; + } + + return new StreamData(url, contentType, stream); + + } + } @Override @@ -205,558 +206,600 @@ public class SignatureTest { public void setSSLSocketFactory(SSLSocketFactory socketFactory) { // TODO Auto-generated method stub - } - - }); - - } - - private Object unmarshal(String file) throws XMLStreamException, JAXBException { - - ClassLoader classLoader = SignatureTest.class.getClassLoader(); - InputStream resourceStream = classLoader.getResourceAsStream(RESOURCE_PREFIX + file); - assertNotNull(resourceStream); - - XMLInputFactory inputFactory = XMLInputFactory.newInstance(); - XMLEventReader eventReader = inputFactory.createXMLEventReader(resourceStream); - RedirectEventFilter redirectEventFilter = new RedirectEventFilter(); - XMLEventReader filteredReader = inputFactory.createFilteredReader(eventReader, redirectEventFilter); - - unmarshaller.setListener(new RedirectUnmarshallerListener(redirectEventFilter)); - - return unmarshaller.unmarshal(filteredReader); - - } - - // - // - // SignatureInfo - // - // - - @SuppressWarnings("unchecked") - private SignatureInfoCreationType unmarshalSignatureInfo(String file) throws JAXBException, XMLStreamException { - - Object object = unmarshal(file); - - Object requestType = ((JAXBElement) object).getValue(); - - assertTrue(requestType instanceof CreateXMLSignatureRequestType); - - SignatureInfoCreationType signatureInfo = ((CreateXMLSignatureRequestType) requestType).getSignatureInfo(); - - assertNotNull(signatureInfo); - - return signatureInfo; - - } - - @Test - public void testSetSignatureInfo_Base64_1() throws JAXBException, SLCommandException, XMLStreamException { - - SignatureInfoCreationType signatureInfo = unmarshalSignatureInfo("SignatureInfo_Base64_1.xml"); - - Signature signature = new Signature(null, new IdValueFactoryImpl(), null); - - signature.setSignatureInfo(signatureInfo); - - Node parent = signature.getParent(); - Node nextSibling = signature.getNextSibling(); - - assertNotNull(parent); - assertTrue("urn:document".equals(parent.getNamespaceURI())); - assertTrue("XMLDocument".equals(parent.getLocalName())); - - assertNotNull(nextSibling); - assertTrue("urn:document".equals(nextSibling.getNamespaceURI())); - assertTrue("Paragraph".equals(nextSibling.getLocalName())); - - } - - @Test - public void testSetSignature_Base64_2() throws JAXBException, SLCommandException, XMLStreamException { - - SignatureInfoCreationType signatureInfo = unmarshalSignatureInfo("SignatureInfo_Base64_2.xml"); - - Signature signature = new Signature(null, new IdValueFactoryImpl(), null); - - signature.setSignatureInfo(signatureInfo); - - Node parent = signature.getParent(); - Node nextSibling = signature.getNextSibling(); - - assertNotNull(parent); - assertTrue("XMLDocument".equals(parent.getLocalName())); - - assertNotNull(nextSibling); - assertTrue("Paragraph".equals(nextSibling.getLocalName())); - - } - - @Test - public void testSetSignature_Base64_3() throws JAXBException, SLCommandException, XMLStreamException { - - SignatureInfoCreationType signatureInfo = unmarshalSignatureInfo("SignatureInfo_Base64_3.xml"); - - Signature signature = new Signature(null, new IdValueFactoryImpl(), null); - - signature.setSignatureInfo(signatureInfo); - - Node parent = signature.getParent(); - Node nextSibling = signature.getNextSibling(); - - assertNotNull(parent); - assertTrue("XMLDocument".equals(parent.getLocalName())); - - assertNotNull(nextSibling); - assertTrue("Paragraph".equals(nextSibling.getLocalName())); - - } - - @Test - public void testSetSignatureInfo_XMLContent_1() throws JAXBException, SLCommandException, XMLStreamException { - - SignatureInfoCreationType signatureInfo = unmarshalSignatureInfo("SignatureInfo_XMLContent_1.xml"); - - Signature signature = new Signature(null, new IdValueFactoryImpl(), null); - - signature.setSignatureInfo(signatureInfo); - - Node parent = signature.getParent(); - Node nextSibling = signature.getNextSibling(); - - assertNotNull(parent); - assertTrue("urn:document".equals(parent.getNamespaceURI())); - assertTrue("Whole".equals(parent.getLocalName())); - - assertNull(nextSibling); - - } - - @Test - public void testSetSignature_Reference_1() throws JAXBException, SLCommandException, XMLStreamException { - - SignatureInfoCreationType signatureInfo = unmarshalSignatureInfo("SignatureInfo_Reference_1.xml"); - - Signature signature = new Signature(null, new IdValueFactoryImpl(), null); - - signature.setSignatureInfo(signatureInfo); - - Node parent = signature.getParent(); - Node nextSibling = signature.getNextSibling(); - - assertNotNull(parent); - assertTrue("urn:document".equals(parent.getNamespaceURI())); - assertTrue("Paragraph".equals(parent.getLocalName())); - - assertNull(nextSibling); - - } - - // - // - // DataObject - // - // - - @SuppressWarnings("unchecked") - private List unmarshalDataObjectInfo(String file) throws JAXBException, XMLStreamException { - - Object object = unmarshal(file); - - Object requestType = ((JAXBElement) object).getValue(); - - assertTrue(requestType instanceof CreateXMLSignatureRequestType); - - List dataObjectInfos = ((CreateXMLSignatureRequestType) requestType).getDataObjectInfo(); - - assertNotNull(dataObjectInfos); - - return dataObjectInfos; - - } - - private void signAndMarshalSignature(Signature signature) throws MarshalException, XMLSignatureException, SLCommandException, SLViewerException { - - Node parent = signature.getParent(); - Node nextSibling = signature.getNextSibling(); - - DOMSignContext signContext = (nextSibling == null) - ? new DOMSignContext(privateKey, parent) - : new DOMSignContext(privateKey, parent, nextSibling); - - signature.sign(signContext); - - Document document = signature.getDocument(); - - DOMImplementationLS domImplLS = DOMUtils.getDOMImplementationLS(); - LSOutput output = domImplLS.createLSOutput(); - output.setByteStream(System.out); - - LSSerializer serializer = domImplLS.createLSSerializer(); -// serializer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE); - serializer.getDomConfig().setParameter("namespaces", Boolean.FALSE); - serializer.write(document, output); - - } - - @SuppressWarnings("unchecked") - @Test - public void testDataObject_Base64Content_1() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { - - List dataObjectInfos = unmarshalDataObjectInfo("DataObjectInfo_Base64Content_1.xml"); - - Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); - - for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { - signature.addDataObject(dataObjectInfo); - } - - signature.setSignerCeritifcate(certificate); - - signature.buildXMLSignature(); - - signAndMarshalSignature(signature); - - List references = signature.getReferences(); - assertTrue(references.size() == 2); - - Reference reference = references.get(0); - assertNotNull(reference.getId()); - - List transforms = reference.getTransforms(); - assertTrue(transforms.size() == 1); - - Transform transform = transforms.get(0); - assertTrue(Transform.BASE64.equals(transform.getAlgorithm())); - - List objects = signature.getXMLObjects(); - assertNotNull(objects); - assertTrue("Size " + objects.size() + " but should be 2.", objects.size() == 2); - - XMLObject object = objects.get(0); - - assertTrue(("#" + object.getId()).equals(reference.getURI())); - - } - - @SuppressWarnings("unchecked") - @Test - public void testDataObject_XMLContent_1() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { - - List dataObjectInfos = unmarshalDataObjectInfo("DataObjectInfo_XMLContent_1.xml"); - - Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); - - for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { - signature.addDataObject(dataObjectInfo); - } - - signature.setSignerCeritifcate(certificate); - - signature.buildXMLSignature(); - - signAndMarshalSignature(signature); - - List references = signature.getReferences(); - assertTrue(references.size() == 2); - - Reference reference = references.get(0); - assertNotNull(reference.getId()); - - List transforms = reference.getTransforms(); - assertTrue(transforms.size() == 2); - - Transform transform = transforms.get(0); - assertTrue(Transform.XPATH2.equals(transform.getAlgorithm())); - - List objects = signature.getXMLObjects(); - assertNotNull(objects); - assertTrue("Size " + objects.size() + " but should be 2.", objects.size() == 2); - - XMLObject object = objects.get(0); - - assertTrue(("#" + object.getId()).equals(reference.getURI())); - - } - - @SuppressWarnings("unchecked") - @Test - public void testDataObject_XMLContent_2() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { - - List dataObjectInfos = unmarshalDataObjectInfo("DataObjectInfo_XMLContent_2.xml"); - - Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); - - for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { - signature.addDataObject(dataObjectInfo); - } - - signature.setSignerCeritifcate(certificate); - - signature.buildXMLSignature(); - - signAndMarshalSignature(signature); - - List references = signature.getReferences(); - assertTrue(references.size() == 2); - - Reference reference = references.get(0); - assertNotNull(reference.getId()); - - List transforms = reference.getTransforms(); - assertTrue(transforms.size() == 2); - - Transform transform = transforms.get(0); - assertTrue(Transform.XPATH2.equals(transform.getAlgorithm())); - - List objects = signature.getXMLObjects(); - assertNotNull(objects); - assertTrue("Size " + objects.size() + " but should be 2.", objects.size() == 2); - - XMLObject object = objects.get(0); - - assertTrue(("#" + object.getId()).equals(reference.getURI())); - - } - - - @SuppressWarnings("unchecked") - @Test - public void testDataObject_LocRefContent_1() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { - - List dataObjectInfos = unmarshalDataObjectInfo("DataObjectInfo_LocRefContent_1.xml"); - - Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); - - for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { - signature.addDataObject(dataObjectInfo); - } - - signature.buildXMLSignature(); - - signAndMarshalSignature(signature); - - List references = signature.getReferences(); - assertTrue(references.size() == 2); - - Reference reference = references.get(0); - assertNotNull(reference.getId()); - - List transforms = reference.getTransforms(); - assertTrue(transforms.size() == 2); - - Transform transform = transforms.get(0); - assertTrue(Transform.XPATH2.equals(transform.getAlgorithm())); - - List objects = signature.getXMLObjects(); - assertNotNull(objects); - assertTrue("Size " + objects.size() + " but should be 2.", objects.size() == 2); - - XMLObject object = objects.get(0); - - assertTrue(("#" + object.getId()).equals(reference.getURI())); - - } - - @SuppressWarnings("unchecked") - @Test - public void testDataObject_LocRefContent_2() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { - - List dataObjectInfos = unmarshalDataObjectInfo("DataObjectInfo_LocRefContent_2.xml"); - - Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); - - for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { - signature.addDataObject(dataObjectInfo); - } - - signature.buildXMLSignature(); - - signAndMarshalSignature(signature); - - List references = signature.getReferences(); - assertTrue(references.size() == 2); - - Reference reference = references.get(0); - assertNotNull(reference.getId()); - - List transforms = reference.getTransforms(); - assertTrue(transforms.size() == 1); - - Transform transform = transforms.get(0); - assertTrue(Transform.BASE64.equals(transform.getAlgorithm())); - - List objects = signature.getXMLObjects(); - assertNotNull(objects); - assertTrue("Size " + objects.size() + " but should be 2.", objects.size() == 2); - - XMLObject object = objects.get(0); - - assertTrue(("#" + object.getId()).equals(reference.getURI())); - - } - - @SuppressWarnings("unchecked") - @Test - public void testDataObject_Reference_1() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { - - List dataObjectInfos = unmarshalDataObjectInfo("DataObjectInfo_Reference_1.xml"); - - Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); - - for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { - signature.addDataObject(dataObjectInfo); - } - - signature.buildXMLSignature(); - - signAndMarshalSignature(signature); - - List references = signature.getReferences(); - assertTrue(references.size() == 2); - - Reference reference = references.get(0); - assertNotNull(reference.getId()); - - List transforms = reference.getTransforms(); - assertTrue(transforms.size() == 1); - - Transform transform = transforms.get(0); - assertTrue(Transform.BASE64.equals(transform.getAlgorithm())); - - List objects = signature.getXMLObjects(); - assertNotNull(objects); - assertTrue("Size " + objects.size() + " but should be 2.", objects.size() == 2); - - XMLObject object = objects.get(0); - - assertTrue(("#" + object.getId()).equals(reference.getURI())); - - } - - @SuppressWarnings("unchecked") - @Test - public void testDataObject_Detached_1() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { - - List dataObjectInfos = unmarshalDataObjectInfo("DataObjectInfo_Detached_1.xml"); - - Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); - - for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { - signature.addDataObject(dataObjectInfo); - } - - signature.buildXMLSignature(); - - signAndMarshalSignature(signature); - - List references = signature.getReferences(); - assertTrue(references.size() == 2); - - Reference reference = references.get(0); - assertNotNull(reference.getId()); - - List transforms = reference.getTransforms(); - assertTrue(transforms.size() == 0); - - List objects = signature.getXMLObjects(); - assertNotNull(objects); - assertTrue(objects.size() == 1); - - } - - @SuppressWarnings("unchecked") - @Test - public void testDataObject_Detached_Base64Content() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { - - List dataObjectInfos = unmarshalDataObjectInfo("DataObjectInfo_Detached_Base64Content.xml"); - - Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); - - for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { - signature.addDataObject(dataObjectInfo); - } - - signature.buildXMLSignature(); - - signAndMarshalSignature(signature); - - List references = signature.getReferences(); - assertTrue(references.size() == 2); - - Reference reference = references.get(0); - assertNotNull(reference.getId()); - - List transforms = reference.getTransforms(); - assertTrue(transforms.size() == 0); - - List objects = signature.getXMLObjects(); - assertNotNull(objects); - assertTrue(objects.size() == 1); - - } - - // - // - // TransformsInfo - // - // - - @SuppressWarnings("unchecked") - private CreateXMLSignatureRequestType unmarshalCreateXMLSignatureRequest(String file) throws JAXBException, XMLStreamException { - - Object object = unmarshal(file); - - Object requestType = ((JAXBElement) object).getValue(); - - assertTrue(requestType instanceof CreateXMLSignatureRequestType); - - return (CreateXMLSignatureRequestType) requestType; - - } - - - @SuppressWarnings("unchecked") - @Test - public void testTransformsInfo_1() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { - - CreateXMLSignatureRequestType requestType = unmarshalCreateXMLSignatureRequest("TransformsInfo_1.xml"); - - Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); - - - signature.setSignatureInfo(requestType.getSignatureInfo()); - - List dataObjectInfos = requestType.getDataObjectInfo(); - - for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { - signature.addDataObject(dataObjectInfo); - } - - signature.setSignerCeritifcate(certificate); - - signature.buildXMLSignature(); - - signAndMarshalSignature(signature); - - List references = signature.getReferences(); - assertTrue(references.size() == 2); - - Reference reference = references.get(0); - assertNotNull(reference.getId()); - - List transforms = reference.getTransforms(); - assertTrue("Size " + transforms.size() + "", transforms.size() == 3); - - Transform transform = transforms.get(0); - assertTrue(Transform.ENVELOPED.equals(transform.getAlgorithm())); - - List objects = signature.getXMLObjects(); - assertNotNull(objects); - assertTrue("Size " + objects.size() + " but should be 1.", objects.size() == 1); - - } - - -} + } + + }); + + } + + private Object unmarshal(String file) throws XMLStreamException, JAXBException { + + ClassLoader classLoader = SignatureTest.class.getClassLoader(); + InputStream resourceStream = classLoader.getResourceAsStream(RESOURCE_PREFIX + file); + assertNotNull(resourceStream); + + XMLInputFactory inputFactory = XMLInputFactory.newInstance(); + XMLEventReader eventReader = inputFactory.createXMLEventReader(resourceStream); + RedirectEventFilter redirectEventFilter = new RedirectEventFilter(); + XMLEventReader filteredReader = inputFactory.createFilteredReader(eventReader, redirectEventFilter); + + unmarshaller.setListener(new RedirectUnmarshallerListener(redirectEventFilter)); + + return unmarshaller.unmarshal(filteredReader); + + } + + // + // + // SignatureInfo + // + // + + @SuppressWarnings("unchecked") + private SignatureInfoCreationType unmarshalSignatureInfo(String file) throws JAXBException, XMLStreamException { + + Object object = unmarshal(file); + + Object requestType = ((JAXBElement) object).getValue(); + + assertTrue(requestType instanceof CreateXMLSignatureRequestType); + + SignatureInfoCreationType signatureInfo = ((CreateXMLSignatureRequestType) requestType).getSignatureInfo(); + + assertNotNull(signatureInfo); + + return signatureInfo; + + } + + @Test + public void testSetSignatureInfo_Base64_1() throws JAXBException, SLCommandException, XMLStreamException { + + SignatureInfoCreationType signatureInfo = unmarshalSignatureInfo("SignatureInfo_Base64_1.xml"); + + Signature signature = new Signature(null, new IdValueFactoryImpl(), null); + + signature.setSignatureInfo(signatureInfo); + + Node parent = signature.getParent(); + Node nextSibling = signature.getNextSibling(); + + assertNotNull(parent); + assertTrue("urn:document".equals(parent.getNamespaceURI())); + assertTrue("XMLDocument".equals(parent.getLocalName())); + + assertNotNull(nextSibling); + assertTrue("urn:document".equals(nextSibling.getNamespaceURI())); + assertTrue("Paragraph".equals(nextSibling.getLocalName())); + + } + + @Test + public void testSetSignature_Base64_2() throws JAXBException, SLCommandException, XMLStreamException { + + SignatureInfoCreationType signatureInfo = unmarshalSignatureInfo("SignatureInfo_Base64_2.xml"); + + Signature signature = new Signature(null, new IdValueFactoryImpl(), null); + + signature.setSignatureInfo(signatureInfo); + + Node parent = signature.getParent(); + Node nextSibling = signature.getNextSibling(); + + assertNotNull(parent); + assertTrue("XMLDocument".equals(parent.getLocalName())); + + assertNotNull(nextSibling); + assertTrue("Paragraph".equals(nextSibling.getLocalName())); + + } + + @Test + public void testSetSignature_Base64_3() throws JAXBException, SLCommandException, XMLStreamException { + + SignatureInfoCreationType signatureInfo = unmarshalSignatureInfo("SignatureInfo_Base64_3.xml"); + + Signature signature = new Signature(null, new IdValueFactoryImpl(), null); + + signature.setSignatureInfo(signatureInfo); + + Node parent = signature.getParent(); + Node nextSibling = signature.getNextSibling(); + + assertNotNull(parent); + assertTrue("XMLDocument".equals(parent.getLocalName())); + + assertNotNull(nextSibling); + assertTrue("Paragraph".equals(nextSibling.getLocalName())); + + } + + @Test + public void testSetSignatureInfo_XMLContent_1() throws JAXBException, SLCommandException, XMLStreamException { + + SignatureInfoCreationType signatureInfo = unmarshalSignatureInfo("SignatureInfo_XMLContent_1.xml"); + + Signature signature = new Signature(null, new IdValueFactoryImpl(), null); + + signature.setSignatureInfo(signatureInfo); + + Node parent = signature.getParent(); + Node nextSibling = signature.getNextSibling(); + + assertNotNull(parent); + assertTrue("urn:document".equals(parent.getNamespaceURI())); + assertTrue("Whole".equals(parent.getLocalName())); + + assertNull(nextSibling); + + } + + @Test + public void testSetSignature_Reference_1() throws JAXBException, SLCommandException, XMLStreamException { + + SignatureInfoCreationType signatureInfo = unmarshalSignatureInfo("SignatureInfo_Reference_1.xml"); + + Signature signature = new Signature(null, new IdValueFactoryImpl(), null); + + signature.setSignatureInfo(signatureInfo); + + Node parent = signature.getParent(); + Node nextSibling = signature.getNextSibling(); + + assertNotNull(parent); + assertTrue("urn:document".equals(parent.getNamespaceURI())); + assertTrue("Paragraph".equals(parent.getLocalName())); + + assertNull(nextSibling); + + } + + // + // + // DataObject + // + // + + @SuppressWarnings("unchecked") + private List unmarshalDataObjectInfo(String file) throws JAXBException, XMLStreamException { + + Object object = unmarshal(file); + + Object requestType = ((JAXBElement) object).getValue(); + + assertTrue(requestType instanceof CreateXMLSignatureRequestType); + + List dataObjectInfos = ((CreateXMLSignatureRequestType) requestType).getDataObjectInfo(); + + assertNotNull(dataObjectInfos); + + return dataObjectInfos; + + } + + private void signAndMarshalSignature(Signature signature) throws MarshalException, XMLSignatureException, SLCommandException, SLViewerException { + + Node parent = signature.getParent(); + Node nextSibling = signature.getNextSibling(); + + DOMSignContext signContext = (nextSibling == null) + ? new DOMSignContext(privateKey, parent) + : new DOMSignContext(privateKey, parent, nextSibling); + + signature.sign(signContext); + + Document document = signature.getDocument(); + + DOMImplementationLS domImplLS = DOMUtils.getDOMImplementationLS(); + LSOutput output = domImplLS.createLSOutput(); + output.setByteStream(System.out); + + LSSerializer serializer = domImplLS.createLSSerializer(); +// serializer.getDomConfig().setParameter("format-pretty-print", Boolean.TRUE); + serializer.getDomConfig().setParameter("namespaces", Boolean.FALSE); + serializer.write(document, output); + + } + + @SuppressWarnings("unchecked") + @Test + public void testDataObject_Base64Content_1() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { + + List dataObjectInfos = unmarshalDataObjectInfo("DataObjectInfo_Base64Content_1.xml"); + + Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); + + for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { + signature.addDataObject(dataObjectInfo); + } + + signature.setSignerCeritifcate(certificate); + + signature.buildXMLSignature(); + + signAndMarshalSignature(signature); + + List references = signature.getReferences(); + assertTrue(references.size() == 2); + + Reference reference = references.get(0); + assertNotNull(reference.getId()); + + List transforms = reference.getTransforms(); + assertTrue(transforms.size() == 1); + + Transform transform = transforms.get(0); + assertTrue(Transform.BASE64.equals(transform.getAlgorithm())); + + List objects = signature.getXMLObjects(); + assertNotNull(objects); + assertTrue("Size " + objects.size() + " but should be 2.", objects.size() == 2); + + XMLObject object = objects.get(0); + + assertTrue(("#" + object.getId()).equals(reference.getURI())); + + } + + @SuppressWarnings("unchecked") + @Test + public void testDataObject_XMLContent_1() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { + + List dataObjectInfos = unmarshalDataObjectInfo("DataObjectInfo_XMLContent_1.xml"); + + Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); + + for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { + signature.addDataObject(dataObjectInfo); + } + + signature.setSignerCeritifcate(certificate); + + signature.buildXMLSignature(); + + signAndMarshalSignature(signature); + + List references = signature.getReferences(); + assertTrue(references.size() == 2); + + Reference reference = references.get(0); + assertNotNull(reference.getId()); + + List transforms = reference.getTransforms(); + assertTrue(transforms.size() == 2); + + Transform transform = transforms.get(0); + assertTrue(Transform.XPATH2.equals(transform.getAlgorithm())); + + List objects = signature.getXMLObjects(); + assertNotNull(objects); + assertTrue("Size " + objects.size() + " but should be 2.", objects.size() == 2); + + XMLObject object = objects.get(0); + + assertTrue(("#" + object.getId()).equals(reference.getURI())); + + } + + @SuppressWarnings("unchecked") + @Test + public void testDataObject_XMLContent_2() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { + + List dataObjectInfos = unmarshalDataObjectInfo("DataObjectInfo_XMLContent_2.xml"); + + Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); + + for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { + signature.addDataObject(dataObjectInfo); + } + + signature.setSignerCeritifcate(certificate); + + signature.buildXMLSignature(); + + signAndMarshalSignature(signature); + + List references = signature.getReferences(); + assertTrue(references.size() == 2); + + Reference reference = references.get(0); + assertNotNull(reference.getId()); + + List transforms = reference.getTransforms(); + assertTrue(transforms.size() == 2); + + Transform transform = transforms.get(0); + assertTrue(Transform.XPATH2.equals(transform.getAlgorithm())); + + List objects = signature.getXMLObjects(); + assertNotNull(objects); + assertTrue("Size " + objects.size() + " but should be 2.", objects.size() == 2); + + XMLObject object = objects.get(0); + + assertTrue(("#" + object.getId()).equals(reference.getURI())); + + } + + + @SuppressWarnings("unchecked") + @Test + public void testDataObject_LocRefContent_1() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { + + List dataObjectInfos = unmarshalDataObjectInfo("DataObjectInfo_LocRefContent_1.xml"); + + Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); + + for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { + signature.addDataObject(dataObjectInfo); + } + + signature.buildXMLSignature(); + + signAndMarshalSignature(signature); + + List references = signature.getReferences(); + assertTrue(references.size() == 2); + + Reference reference = references.get(0); + assertNotNull(reference.getId()); + + List transforms = reference.getTransforms(); + assertTrue(transforms.size() == 2); + + Transform transform = transforms.get(0); + assertTrue(Transform.XPATH2.equals(transform.getAlgorithm())); + + List objects = signature.getXMLObjects(); + assertNotNull(objects); + assertTrue("Size " + objects.size() + " but should be 2.", objects.size() == 2); + + XMLObject object = objects.get(0); + + assertTrue(("#" + object.getId()).equals(reference.getURI())); + + } + + @SuppressWarnings("unchecked") + @Test + public void testDataObject_LocRefContent_2() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { + + List dataObjectInfos = unmarshalDataObjectInfo("DataObjectInfo_LocRefContent_2.xml"); + + Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); + + for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { + signature.addDataObject(dataObjectInfo); + } + + signature.buildXMLSignature(); + + signAndMarshalSignature(signature); + + List references = signature.getReferences(); + assertTrue(references.size() == 2); + + Reference reference = references.get(0); + assertNotNull(reference.getId()); + + List transforms = reference.getTransforms(); + assertTrue(transforms.size() == 1); + + Transform transform = transforms.get(0); + assertTrue(Transform.BASE64.equals(transform.getAlgorithm())); + + List objects = signature.getXMLObjects(); + assertNotNull(objects); + assertTrue("Size " + objects.size() + " but should be 2.", objects.size() == 2); + + XMLObject object = objects.get(0); + + assertTrue(("#" + object.getId()).equals(reference.getURI())); + + } + + @SuppressWarnings("unchecked") + @Test + public void testDataObject_Reference_1() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { + + List dataObjectInfos = unmarshalDataObjectInfo("DataObjectInfo_Reference_1.xml"); + + Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); + + for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { + signature.addDataObject(dataObjectInfo); + } + + signature.buildXMLSignature(); + + signAndMarshalSignature(signature); + + List references = signature.getReferences(); + assertTrue(references.size() == 2); + + Reference reference = references.get(0); + assertNotNull(reference.getId()); + + List transforms = reference.getTransforms(); + assertTrue(transforms.size() == 1); + + Transform transform = transforms.get(0); + assertTrue(Transform.BASE64.equals(transform.getAlgorithm())); + + List objects = signature.getXMLObjects(); + assertNotNull(objects); + assertTrue("Size " + objects.size() + " but should be 2.", objects.size() == 2); + + XMLObject object = objects.get(0); + + assertTrue(("#" + object.getId()).equals(reference.getURI())); + + } + + @SuppressWarnings("unchecked") + @Test + public void testDataObject_Detached_1() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { + + List dataObjectInfos = unmarshalDataObjectInfo("DataObjectInfo_Detached_1.xml"); + + Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); + + for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { + signature.addDataObject(dataObjectInfo); + } + + signature.buildXMLSignature(); + + signAndMarshalSignature(signature); + + List references = signature.getReferences(); + assertTrue(references.size() == 2); + + Reference reference = references.get(0); + assertNotNull(reference.getId()); + + List transforms = reference.getTransforms(); + assertTrue(transforms.size() == 0); + + List objects = signature.getXMLObjects(); + assertNotNull(objects); + assertTrue(objects.size() == 1); + + } + + @SuppressWarnings("unchecked") + @Test + public void testDataObject_Detached_Base64Content() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { + + List dataObjectInfos = unmarshalDataObjectInfo("DataObjectInfo_Detached_Base64Content.xml"); + + Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); + + for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { + signature.addDataObject(dataObjectInfo); + } + + signature.buildXMLSignature(); + + signAndMarshalSignature(signature); + + List references = signature.getReferences(); + assertTrue(references.size() == 2); + + Reference reference = references.get(0); + assertNotNull(reference.getId()); + + List transforms = reference.getTransforms(); + assertTrue(transforms.size() == 0); + + List objects = signature.getXMLObjects(); + assertNotNull(objects); + assertTrue(objects.size() == 1); + + } + + // + // + // TransformsInfo + // + // + + @SuppressWarnings("unchecked") + private CreateXMLSignatureRequestType unmarshalCreateXMLSignatureRequest(String file) throws JAXBException, XMLStreamException { + + Object object = unmarshal(file); + + Object requestType = ((JAXBElement) object).getValue(); + + assertTrue(requestType instanceof CreateXMLSignatureRequestType); + + return (CreateXMLSignatureRequestType) requestType; + + } + + + @SuppressWarnings("unchecked") + @Test + public void testTransformsInfo_1() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { + + CreateXMLSignatureRequestType requestType = unmarshalCreateXMLSignatureRequest("TransformsInfo_1.xml"); + + Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); + + + signature.setSignatureInfo(requestType.getSignatureInfo()); + + List dataObjectInfos = requestType.getDataObjectInfo(); + + for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { + signature.addDataObject(dataObjectInfo); + } + + signature.setSignerCeritifcate(certificate); + + signature.buildXMLSignature(); + + signAndMarshalSignature(signature); + + List references = signature.getReferences(); + assertTrue(references.size() == 2); + + Reference reference = references.get(0); + assertNotNull(reference.getId()); + + List transforms = reference.getTransforms(); + assertTrue("Size " + transforms.size() + "", transforms.size() == 3); + + Transform transform = transforms.get(0); + assertTrue(Transform.ENVELOPED.equals(transform.getAlgorithm())); + + List objects = signature.getXMLObjects(); + assertNotNull(objects); + assertTrue("Size " + objects.size() + " but should be 1.", objects.size() == 1); + + } + + @SuppressWarnings("unchecked") + @Test + @Ignore + public void testTransformsInfo_2() throws JAXBException, SLCommandException, XMLStreamException, SLRequestException, MarshalException, XMLSignatureException, SLViewerException { + + CreateXMLSignatureRequestType requestType = unmarshalCreateXMLSignatureRequest("TransformsInfo_2.xml"); + + Signature signature = new Signature(null, new IdValueFactoryImpl(), new AlgorithmMethodFactoryImpl()); + + + signature.setSignatureInfo(requestType.getSignatureInfo()); + + List dataObjectInfos = requestType.getDataObjectInfo(); + + for (DataObjectInfoType dataObjectInfo : dataObjectInfos) { + signature.addDataObject(dataObjectInfo); + } + + signature.setSignerCeritifcate(certificate); + + signature.buildXMLSignature(); + + signAndMarshalSignature(signature); + + List references = signature.getReferences(); + assertTrue(references.size() == 2); + + Reference reference = references.get(0); + assertNotNull(reference.getId()); + + List transforms = reference.getTransforms(); + assertTrue("Size " + transforms.size() + "", transforms.size() == 2); + + Transform transform = transforms.get(0); + assertTrue(Transform.XSLT.equals(transform.getAlgorithm())); + + List objects = signature.getXMLObjects(); + assertNotNull(objects); + assertTrue("Size " + objects.size() + " but should be 1.", objects.size() == 1); + + } + + +} diff --git a/bkucommon/src/test/resources/at/gv/egiz/bku/slcommands/impl/TransformsInfo_2.xml b/bkucommon/src/test/resources/at/gv/egiz/bku/slcommands/impl/TransformsInfo_2.xml new file mode 100644 index 00000000..f43dc61a --- /dev/null +++ b/bkucommon/src/test/resources/at/gv/egiz/bku/slcommands/impl/TransformsInfo_2.xml @@ -0,0 +1,397 @@ + + + SecureSignatureKeypair + + + + + + + + + + + Signatur der Anmeldedaten + + + +

Signatur der Anmeldedaten

+

+

Mit meiner elektronischen Signatur beantrage ich, + + , geboren am . . , in der Rolle als (OID***= ), den Zugang zur gesicherten Anwendung.

+

+

Datum und Uhrzeit: . . , : + : +

+ +

HPI(**):

+
+ +

wbPK(*):

+
+ +
+

Ich bin weiters ermächtigt als von + , geboren am . . + + , + , in deren Auftrag zu handeln. +

wbPK(*) des Vollmachtgebers:

+
+

+

+ + + +

+


+ + +

+


+
+ +

+


+
+ + +
(*) wbPK: Das wirtschaftsbereichsspezifische + Personenkennzeichen wird aus den jeweiligen + Stammzahlen des Bürgers und des Wirtschaftsunternehmens + berechnet und ermöglicht eine eindeutige Zuordnung des + Bürgers zum Wirtschaftsunternehmen.
+
+ +
(**) HPI: Der eHealth Professional + Identifier wird aus den jeweiligen Stammzahlen + der Gesundheitsdiensteanbieterinnen / + Gesundheitsdiensteanbieter berechnet und ermöglicht eine + eindeutige Zuordnung der Gesundheitsdiensteanbieterin / + des Gesundheitsdiensteanbieters im + Gesundheitsbereich.
+
+ +
(***) OID: Object Identifier sind + standardisierte Objekt-Bezeichner und beschreiben + eindeutig die Rollen des GDA-Token Inhabers.
+
+ + +
+
+
+ +
+ + application/xhtml+xml + +
+ + + + + + + + + + Signatur der Anmeldedaten + + +

Signatur der Anmeldedaten

+

+

Mit meiner elektronischen Signatur beantrage ich, + + , geboren am . . , in der Rolle als (OID***= ), den Zugang zur gesicherten Anwendung.

+

+

Datum und Uhrzeit: . . , : + : +

+ +

HPI(**):

+
+ +

wbPK(*):

+
+ +
+

Ich bin weiters ermächtigt als von + , geboren am . . + + , + , in deren Auftrag zu handeln. +

wbPK(*) des Vollmachtgebers:

+
+

+

+ + + +

+


+ + +

+


+
+ +

+


+
+ + +
(*) wbPK: Das wirtschaftsbereichsspezifische + Personenkennzeichen wird aus den jeweiligen + Stammzahlen des Bürgers und des Wirtschaftsunternehmens + berechnet und ermöglicht eine eindeutige Zuordnung des + Bürgers zum Wirtschaftsunternehmen.
+
+ +
(**) HPI: Der eHealth Professional Identifier + wird aus den jeweiligen Stammzahlen der + Gesundheitsdiensteanbieterinnen / + Gesundheitsdiensteanbieter berechnet und ermöglicht eine + eindeutige Zuordnung der Gesundheitsdiensteanbieterin / + des Gesundheitsdiensteanbieters im + Gesundheitsbereich.
+
+ +
(***) OID: Object Identifier sind standardisierte + Objekt-Bezeichner und beschreiben eindeutig die Rollen + des GDA-Token Inhabers.
+
+ + +
+
+
+ +
+ + text/html + +
+ + + + + + + + Mit meiner elektronischen Signatur beantrage ich, + + , geboren am + + . + + . + + , + + in der Rolle als + + (OID***= + ) + , + + den Zugang zur gesicherten Anwendung. + Datum und Uhrzeit: + + . + + . + + , + + : + + : + + + + HPI(**): + + + + + wbPK(*): + + + + + Ich bin weiters ermächtigt als + + von + + + , geboren am + + . + + . + + + + , + + + , in deren Auftrag zu handeln. + + + wbPK(*) des Vollmachtgebers: + + + + + + (*) wbPK: Das wirtschaftsbereichsspezifische Personenkennzeichen wird aus den jeweiligen Stammzahlen des Bürgers und des Wirtschaftsunternehmens berechnet und ermöglicht eine eindeutige Zuordnung des Bürgers zum Wirtschaftsunternehmen. + + + (**) HPI: Der eHealth Professional Identifier wird aus den jeweiligen Stammzahlen der Gesundheitsdiensteanbieterinnen / Gesundheitsdiensteanbieter berechnet und ermöglicht eine eindeutige Zuordnung der Gesundheitsdiensteanbieterin / des Gesundheitsdiensteanbieters im Gesundheitsbereich. + + + (***) OID: Object Identifier sind standardisierte Objekt-Bezeichner und beschreiben eindeutig die Rollen des GDA-Token Inhabers. + + + + + + + not(text()) + + + + text/plain + + +
+ + + + + + + https://demo.egiz.gv.at/exchange-moa-id-auth/ + + + + + LTpz8VYzns2jrx0J8Gm/R/nAhxA= + urn:publicid:gv.at:wbpk+FN+TODO + + + + + https://apps.egiz.gv.at/urlaubsschein-frontend/moaid-login + + + 1971-11-10 + + + + + + /saml:Assertion + +
diff --git a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java index 13c57686..86223854 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/ACOSCard.java @@ -30,8 +30,6 @@ package at.gv.egiz.smcc; import java.nio.charset.Charset; -import java.util.logging.Level; -import java.util.logging.Logger; import javax.smartcardio.CardChannel; import javax.smartcardio.CardException; import javax.smartcardio.CommandAPDU; @@ -41,7 +39,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class ACOSCard extends AbstractSignatureCard implements SignatureCard { - + private static Log log = LogFactory.getLog(ACOSCard.class); public static final byte[] AID_DEC = new byte[] { (byte) 0xA0, (byte) 0x00, @@ -100,8 +98,15 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { (byte) 0x01 // RSA // TODO: Not verified yet }; + private static final int PINSPEC_INF = 0; + private static final int PINSPEC_DEC = 1; + private static final int PINSPEC_SIG = 2; + public ACOSCard() { super("at/gv/egiz/smcc/ACOSCard"); + pinSpecs.add(PINSPEC_INF, new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("inf.pin.name"), KID_PIN_INF, null)); + pinSpecs.add(PINSPEC_DEC, new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("dec.pin.name"), KID_PIN_DEC, null)); + pinSpecs.add(PINSPEC_SIG, new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name"), KID_PIN_SIG, null)); } /* (non-Javadoc) @@ -165,7 +170,8 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { try { if ("IdentityLink".equals(infobox)) { - PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("inf.pin.name")); + PINSpec spec = pinSpecs.get(PINSPEC_INF); + //new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("inf.pin.name")); int retries = -1; String pin = null; @@ -219,7 +225,8 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { - PINSpec spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name")); + PINSpec spec = pinSpecs.get(PINSPEC_SIG); + //new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name")); int retries = -1; String pin = null; @@ -260,7 +267,8 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) { - PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("dec.pin.name")); + PINSpec spec = pinSpecs.get(PINSPEC_DEC); + //new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("dec.pin.name")); int retries = -1; String pin = null; @@ -321,11 +329,6 @@ public class ACOSCard extends AbstractSignatureCard implements SignatureCard { 0x00, fid, 256)); } - @Override - public byte[] getKIDs() { - return new byte[] { KID_PIN_DEC, KID_PIN_INF, KID_PIN_SIG }; - } - @Override public int verifyPIN(String pin, byte kid) throws LockedException, NotActivatedException, SignatureCardException { diff --git a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java index 67f090a5..cb068725 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/AbstractSignatureCard.java @@ -31,7 +31,11 @@ package at.gv.egiz.smcc; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.ResourceBundle; import javax.smartcardio.ATR; @@ -49,6 +53,8 @@ public abstract class AbstractSignatureCard implements SignatureCard { private static Log log = LogFactory.getLog(AbstractSignatureCard.class); + protected List pinSpecs = new ArrayList(); + private ResourceBundle i18n; private String resourceBundleName; @@ -433,4 +439,8 @@ public abstract class AbstractSignatureCard implements SignatureCard { } } + @Override + public List getPINSpecs() { + return pinSpecs; + } } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java b/smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java index 0852d664..d180ddf0 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/PINSpec.java @@ -35,23 +35,40 @@ public class PINSpec { String name_; + byte kid_; + + byte[] context_aid_; + + /** + * + * @param minLenght + * @param maxLength + * @param rexepPattern + * @param resourceBundle + * @param name + * @param kid the keyId for this pin + */ public PINSpec(int minLenght, int maxLength, String rexepPattern, - ResourceBundle resourceBundle, String name) { + ResourceBundle resourceBundle, String name, byte kid, byte[] contextAID) { minLength_ = minLenght; maxLength_ = maxLength; rexepPattern_ = rexepPattern; resourceBundle_ = resourceBundle; name_ = name; + kid_ = kid; + context_aid_ = contextAID; } public PINSpec(int minLenght, int maxLength, String rexepPattern, - String name) { + String name, byte kid, byte[] contextAID) { minLength_ = minLenght; maxLength_ = maxLength; rexepPattern_ = rexepPattern; name_ = name; + kid_ = kid; + context_aid_ = contextAID; } @@ -75,7 +92,14 @@ public class PINSpec { public String getRexepPattern() { return rexepPattern_; } - + + public byte getKID() { + return kid_; + } + + public byte[] getContextAID() { + return context_aid_; + } } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java index e80c6683..ae43629e 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/STARCOSCard.java @@ -29,10 +29,8 @@ package at.gv.egiz.smcc; import java.math.BigInteger; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import javax.smartcardio.CardChannel; import javax.smartcardio.CardException; import javax.smartcardio.CommandAPDU; @@ -42,7 +40,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class STARCOSCard extends AbstractSignatureCard implements SignatureCard { - + /** * Logging facility. */ @@ -155,12 +153,17 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard }; public static final byte KID_PIN_CARD = (byte) 0x01; - + + private static final int PINSPEC_CARD = 0; + private static final int PINSPEC_SS = 1; + /** * Creates an new instance. */ public STARCOSCard() { super("at/gv/egiz/smcc/STARCOSCard"); + pinSpecs.add(PINSPEC_CARD, new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name"), KID_PIN_CARD, null)); + pinSpecs.add(PINSPEC_SS, new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name"), KID_PIN_SS, AID_DF_SS)); } @Override @@ -210,7 +213,8 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard try { if ("IdentityLink".equals(infobox)) { - PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); + PINSpec spec = pinSpecs.get(PINSPEC_CARD); + //new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); int retries = -1; String pin = null; @@ -302,7 +306,8 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard if (KeyboxName.SECURE_SIGNATURE_KEYPAIR.equals(keyboxName)) { - PINSpec spec = new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name")); + PINSpec spec = pinSpecs.get(PINSPEC_SS); + //new PINSpec(6, 10, "[0-9]", getResourceBundle().getString("sig.pin.name")); int retries = -1; String pin = null; @@ -334,7 +339,8 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard } else if (KeyboxName.CERITIFIED_KEYPAIR.equals(keyboxName)) { - PINSpec spec = new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); + PINSpec spec = pinSpecs.get(PINSPEC_CARD); + //new PINSpec(4, 4, "[0-9]", getResourceBundle().getString("card.pin.name")); int retries = -1; String pin = null; @@ -455,11 +461,6 @@ public class STARCOSCard extends AbstractSignatureCard implements SignatureCard } } - @Override - public byte[] getKIDs() { - return new byte[] { KID_PIN_CARD, KID_PIN_SS }; - } - /** * VERIFY PIN *

diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java index bad7ccf6..8dc4ac2a 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SWCard.java @@ -36,9 +36,13 @@ import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; +import java.util.ArrayList; import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; import java.util.Locale; +import java.util.Map; import javax.smartcardio.Card; import javax.smartcardio.CardTerminal; @@ -311,7 +315,7 @@ public class SWCard implements SignatureCard { if (password == null) { - PINSpec pinSpec = new PINSpec(0, -1, ".", "KeyStore-Password"); + PINSpec pinSpec = new PINSpec(0, -1, ".", "KeyStore-Password", (byte) 0x01, null); password = provider.providePIN(pinSpec, -1); @@ -390,13 +394,13 @@ public class SWCard implements SignatureCard { } @Override - public byte[] getKIDs() { - return null; + public int verifyPIN(String pin, byte kid) throws LockedException, NotActivatedException, SignatureCardException { + return -1; } @Override - public int verifyPIN(String pin, byte kid) throws LockedException, NotActivatedException, SignatureCardException { - return -1; + public List getPINSpecs() { + return new ArrayList(); } } diff --git a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java index 1ec35b78..1e5e09c8 100644 --- a/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java +++ b/smcc/src/main/java/at/gv/egiz/smcc/SignatureCard.java @@ -31,6 +31,7 @@ package at.gv.egiz.smcc; import java.util.List; import java.util.Locale; +import java.util.Map; import javax.smartcardio.Card; import javax.smartcardio.CardTerminal; @@ -118,10 +119,10 @@ public interface SignatureCard { PINProvider provider) throws SignatureCardException, InterruptedException; /** - * get the KIDs for the availabel PINs + * Get the KIDs for all available PINs and the corresponding PINSpecs * @return array of KIDs */ - public byte[] getKIDs(); + public List getPINSpecs(); /** * diff --git a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/AbstractBKUWorker.java b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/AbstractBKUWorker.java index e10ba8f9..b6c5a8ca 100644 --- a/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/AbstractBKUWorker.java +++ b/smccSTAL/src/main/java/at/gv/egiz/bku/smccstal/AbstractBKUWorker.java @@ -102,6 +102,9 @@ public abstract class AbstractBKUWorker extends AbstractSMCCSTAL implements Acti @Override protected boolean waitForCard() { + if (signatureCard != null) { + return false; + } SMCCHelper smccHelper = new SMCCHelper(); actionCommandList.clear(); actionCommandList.add("cancel"); diff --git a/smccSTAL/src/test/java/at/gv/egiz/smcc/AbstractSMCCSTALTest.java b/smccSTAL/src/test/java/at/gv/egiz/smcc/AbstractSMCCSTALTest.java index a5f8a771..a5d6df23 100644 --- a/smccSTAL/src/test/java/at/gv/egiz/smcc/AbstractSMCCSTALTest.java +++ b/smccSTAL/src/test/java/at/gv/egiz/smcc/AbstractSMCCSTALTest.java @@ -86,15 +86,16 @@ public class AbstractSMCCSTALTest extends AbstractSMCCSTAL implements } - @Override - public byte[] getKIDs() { - return null; - } @Override public int verifyPIN(String pin, byte kid) throws LockedException, NotActivatedException, SignatureCardException { return 0; } + + @Override + public List getPINSpecs() { + return new ArrayList(); + } }; return false; diff --git a/utils/src/main/java/at/gv/egiz/marshal/NamespacePrefix.java b/utils/src/main/java/at/gv/egiz/marshal/NamespacePrefix.java new file mode 100644 index 00000000..c03f17cd --- /dev/null +++ b/utils/src/main/java/at/gv/egiz/marshal/NamespacePrefix.java @@ -0,0 +1,34 @@ +/* + * 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.marshal; + +/** + * + * @author Clemens Orthacker + */ +public interface NamespacePrefix { + String CARDCHANNEL_PREFIX = "cc"; + String ECDSA_PREFIX = "ecdsa"; + String PERSONDATA_PREFIX = "pr"; + String SAML10_PREFIX = "saml"; + String SL_PREFIX = "sl"; + String XADES_PREFIX = "xades"; + String XMLDSIG_PREFIX = "xmldsig"; + String XSI_PREFIX = "xsi"; + +} diff --git a/utils/src/main/java/at/gv/egiz/marshal/NamespacePrefixMapperImpl.java b/utils/src/main/java/at/gv/egiz/marshal/NamespacePrefixMapperImpl.java index a08c1188..519f6b1f 100644 --- a/utils/src/main/java/at/gv/egiz/marshal/NamespacePrefixMapperImpl.java +++ b/utils/src/main/java/at/gv/egiz/marshal/NamespacePrefixMapperImpl.java @@ -36,35 +36,35 @@ public class NamespacePrefixMapperImpl extends NamespacePrefixMapper { log.trace("prefix for namespace " + namespaceUri + " requested"); } if ("http://www.w3.org/2001/XMLSchema-instance".equals(namespaceUri)) { - return "xsi"; + return NamespacePrefix.XSI_PREFIX; } if ("http://www.w3.org/2000/09/xmldsig#".equals(namespaceUri)) { - return "dsig"; + return NamespacePrefix.XMLDSIG_PREFIX; } if ("http://www.buergerkarte.at/namespaces/securitylayer/1.2#".equals(namespaceUri)) { - return "sl"; + return NamespacePrefix.SL_PREFIX; } if ("http://www.buergerkarte.at/cardchannel".equals(namespaceUri)) { - return "cc"; + return NamespacePrefix.CARDCHANNEL_PREFIX; } if ("http://www.w3.org/2001/04/xmldsig-more#".equals(namespaceUri)) { - return "ecdsa"; + return NamespacePrefix.ECDSA_PREFIX; } if ("http://reference.e-government.gv.at/namespace/persondata/20020228#".equals(namespaceUri)) { - return "pr"; + return NamespacePrefix.PERSONDATA_PREFIX; } if ("urn:oasis:names:tc:SAML:1.0:assertion".equals(namespaceUri)) { - return "saml"; + return NamespacePrefix.SAML10_PREFIX; } if ("http://uri.etsi.org/01903/v1.1.1#".equals(namespaceUri)) { - return "xades"; + return NamespacePrefix.XADES_PREFIX; } return suggestion; diff --git a/utils/src/main/java/at/gv/egiz/slbinding/RedirectEventFilter.java b/utils/src/main/java/at/gv/egiz/slbinding/RedirectEventFilter.java index d2a7fb30..14c5ba48 100644 --- a/utils/src/main/java/at/gv/egiz/slbinding/RedirectEventFilter.java +++ b/utils/src/main/java/at/gv/egiz/slbinding/RedirectEventFilter.java @@ -1,19 +1,19 @@ /* -* 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. -*/ + * 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. + */ /* * To change this template, choose Tools | Templates * and open the template in the editor. @@ -33,79 +33,84 @@ import javax.xml.stream.events.XMLEvent; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +/* + * TODO: don't set redirect stream from caller (caller does not know whether redirection will be triggered) + * rather create on trigger and pass to caller + */ public class RedirectEventFilter implements EventFilter { - public static final String DEFAULT_ENCODING = "UTF-8"; - private static Log log = LogFactory.getLog(RedirectEventFilter.class); - protected XMLEventWriter redirectWriter = null; - protected Set redirectTriggers = null; - private int depth = -1; - protected NamespaceContext currentNamespaceContext = null; + public static final String DEFAULT_ENCODING = "UTF-8"; + private static Log log = LogFactory.getLog(RedirectEventFilter.class); + protected XMLEventWriter redirectWriter = null; + protected Set redirectTriggers = null; + private int depth = -1; + protected NamespaceContext currentNamespaceContext = null; - /** - * Event redirection is disabled, set a redirect stream to enable. - */ - public RedirectEventFilter() { - redirectWriter = null; - // redirectTriggers = null; - } + /** + * Event redirection is disabled, set a redirect stream to enable. + */ + public RedirectEventFilter() { + redirectWriter = null; + // redirectTriggers = null; + } - /** - * - * @param redirectStream - * if null, no events are redirected - * @param redirectTriggers - * if null, all events are redirected - */ - public RedirectEventFilter(OutputStream redirectStream, String encoding) - throws XMLStreamException { // , List redirectTriggers - if (redirectStream != null) { - XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); - if (encoding == null) { - encoding = DEFAULT_ENCODING; - } - this.redirectWriter = outputFactory.createXMLEventWriter(redirectStream, + /** + * + * @param redirectStream + * if null, no events are redirected + * @param redirectTriggers + * if null, all events are redirected + */ + public RedirectEventFilter(OutputStream redirectStream, String encoding) + throws XMLStreamException { // , List redirectTriggers + if (redirectStream != null) { + XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); + if (encoding == null) { + encoding = DEFAULT_ENCODING; + } + this.redirectWriter = outputFactory.createXMLEventWriter(redirectStream, encoding); - } - // this.redirectTriggers = redirectTriggers; } + // this.redirectTriggers = redirectTriggers; + } - /** - * All startElement events occuring in the redirectTriggers list will trigger - * redirection of the entire (sub-)fragment. - * - * @param event - * @return false if an event is redirected - */ - @Override - public boolean accept(XMLEvent event) { - int eventType = event.getEventType(); + /** + * All startElement events occuring in the redirectTriggers list will trigger + * redirection of the entire (sub-)fragment. + * + * @param event + * @return false if an event is redirected + */ + @Override + public boolean accept(XMLEvent event) { + int eventType = event.getEventType(); - if (eventType == XMLStreamConstants.START_ELEMENT) { - currentNamespaceContext = event.asStartElement().getNamespaceContext(); - } - if (redirectWriter == null) { - return true; - } - if (eventType == XMLStreamConstants.START_ELEMENT) { - if (depth >= 0 || triggersRedirect(event.asStartElement().getName())) { - depth++; - } - } else if (eventType == XMLStreamConstants.END_ELEMENT) { - if (depth >= 0 && --depth < 0) { - // redirect the end element of the trigger, - // but do not redirect the end element of the calling type - if (redirectTriggers != null) { - redirectEvent(event); - return false; - } - } - } - if (depth >= 0) { //|| (depth == 0 && redirectTriggers == null)) { - redirectEvent(event); - return false; + if (eventType == XMLStreamConstants.START_ELEMENT) { + //hopefully, this is a copy + currentNamespaceContext = event.asStartElement().getNamespaceContext(); + } + if (redirectWriter == null) { + return true; + } + if (eventType == XMLStreamConstants.START_ELEMENT) { + if (depth >= 0 || triggersRedirect(event.asStartElement().getName())) { + depth++; + } + } else if (eventType == XMLStreamConstants.END_ELEMENT) { + if (depth >= 0 && --depth < 0) { + // redirect the end element of the trigger, + // but do not redirect the end element of the calling type + if (redirectTriggers != null) { + redirectEvent(event); + return false; } - return true; // depth < 0; + } + } + if (depth >= 0) { //|| (depth == 0 && redirectTriggers == null)) { + redirectEvent(event); + return false; + } + return true; // depth < 0; // switch (event.getEventType()) { // case XMLStreamConstants.START_ELEMENT: @@ -132,128 +137,130 @@ public class RedirectEventFilter implements EventFilter { // return false; // } // return true; // depth < 0; - } + } - /** - * @param startElt - * @return true if the set of triggers contains startElement - * (or no triggers are registered, i.e. everything is redirected) - */ - private boolean triggersRedirect(QName startElement) { - if (redirectTriggers != null) { - return redirectTriggers.contains(startElement); - } - return true; + /** + * @param startElt + * @return true if the set of triggers contains startElement + * (or no triggers are registered, i.e. everything is redirected) + */ + private boolean triggersRedirect(QName startElement) { + if (redirectTriggers != null) { + return redirectTriggers.contains(startElement); } + return true; + } - private void redirectEvent(XMLEvent event) { - try { - if (log.isTraceEnabled()) { - log.trace("redirecting StAX event " + event); - } - redirectWriter.add(event); - } catch (XMLStreamException ex) { - ex.printStackTrace(); - } + private void redirectEvent(XMLEvent event) { + try { + if (log.isTraceEnabled()) { + log.trace("redirecting StAX event " + event); + } + redirectWriter.add(event); + } catch (XMLStreamException ex) { + ex.printStackTrace(); } + } - /** - * Enable/disable redirection of all events from now on. - * The redirected events will be UTF-8 encoded and written to the stream. - * - * @param redirectstream - * if null, redirection is disabled - */ - public void setRedirectStream(OutputStream redirectStream) throws XMLStreamException { - setRedirectStream(redirectStream, DEFAULT_ENCODING, null); - } + /** + * Enable/disable redirection of all events from now on. + * The redirected events will be UTF-8 encoded and written to the stream. + * + * @param redirectstream + * if null, redirection is disabled + */ + public void setRedirectStream(OutputStream redirectStream) throws XMLStreamException { + setRedirectStream(redirectStream, DEFAULT_ENCODING, null); + } - /** - * Enable/disable redirection of all events from now on. - * - * @param redirectStream if null, redirection is disabled - * @param encoding The encoding for the redirect stream - * @throws javax.xml.stream.XMLStreamException - */ - public void setRedirectStream(OutputStream redirectStream, String encoding) throws XMLStreamException { - setRedirectStream(redirectStream, encoding, null); - } + /** + * Enable/disable redirection of all events from now on. + * + * @param redirectStream if null, redirection is disabled + * @param encoding The encoding for the redirect stream + * @throws javax.xml.stream.XMLStreamException + */ + public void setRedirectStream(OutputStream redirectStream, String encoding) throws XMLStreamException { + setRedirectStream(redirectStream, encoding, null); + } - /** - * Enable/disable redirection of all (child) elements contained in redirect triggers. - * The redirected events will be UTF-8 encoded and written to the stream. - * - * @param redirectstream - * if null, redirection is disabled - * @param redirectTriggers elements that trigger the redirection - */ - public void setRedirectStream(OutputStream redirectStream, Set redirectTriggers) throws XMLStreamException { - setRedirectStream(redirectStream, DEFAULT_ENCODING, redirectTriggers); - } + /** + * Enable/disable redirection of all (child) elements contained in redirect triggers. + * The redirected events will be UTF-8 encoded and written to the stream. + * + * @param redirectstream + * if null, redirection is disabled + * @param redirectTriggers elements that trigger the redirection + */ + public void setRedirectStream(OutputStream redirectStream, Set redirectTriggers) throws XMLStreamException { + setRedirectStream(redirectStream, DEFAULT_ENCODING, redirectTriggers); + } - /** - * Enable/disable redirection of all (child) elements contained in redirect triggers. - * - * @param redirectstream - * if null, redirection is disabled - * @param encoding The encoding for the redirect stream - * @param redirectTriggers elements that trigger the redirection - */ - public void setRedirectStream(OutputStream redirectStream, String encoding, Set redirectTriggers) throws XMLStreamException { - if (redirectStream != null) { - XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); - if (encoding == null) { - encoding = DEFAULT_ENCODING; - } - redirectWriter = outputFactory.createXMLEventWriter(redirectStream, + /** + * Enable/disable redirection of all (child) elements contained in redirect triggers. + * + * TODO: don't set redirect stream from caller (caller does not know whether redirection will be triggered) + * rather create on trigger and pass to caller + * @param redirectstream + * if null, redirection is disabled + * @param encoding The encoding for the redirect stream + * @param redirectTriggers elements that trigger the redirection + */ + public void setRedirectStream(OutputStream redirectStream, String encoding, Set redirectTriggers) throws XMLStreamException { + if (redirectStream != null) { + XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); + if (encoding == null) { + encoding = DEFAULT_ENCODING; + } + redirectWriter = outputFactory.createXMLEventWriter(redirectStream, encoding); - if (redirectTriggers == null) { - // start redirecting - depth = 0; - } - this.redirectTriggers = redirectTriggers; - } else { - redirectWriter = null; - this.redirectTriggers = null; - } + if (redirectTriggers == null) { + // start redirecting + depth = 0; + } + this.redirectTriggers = redirectTriggers; + } else { + redirectWriter = null; + this.redirectTriggers = null; } + } - /** - * Enable/disable redirection of fragments (defined by elements in - * redirectTriggers) - * - * @param redirectStream - * if null, redirection is disabled - * @param redirectTriggers - * All startElement events occuring in this list will trigger - * redirection of the entire fragment. If null, all events are - * redirected - */ - // public void setRedirectStream(OutputStream redirectStream, List - // redirectTriggers) throws XMLStreamException { - // if (redirectStream != null) { - // XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); - // redirectWriter = outputFactory.createXMLEventWriter(redirectStream); - // } else { - // redirectWriter = null; - // } - // this.redirectTriggers = (redirectStream == null) ? null : redirectTriggers; - // } - /** - * flushes the internal EventWriter - * - * @throws javax.xml.stream.XMLStreamException - */ - public void flushRedirectStream() throws XMLStreamException { - redirectWriter.flush(); - } + /** + * Enable/disable redirection of fragments (defined by elements in + * redirectTriggers) + * + * @param redirectStream + * if null, redirection is disabled + * @param redirectTriggers + * All startElement events occuring in this list will trigger + * redirection of the entire fragment. If null, all events are + * redirected + */ + // public void setRedirectStream(OutputStream redirectStream, List + // redirectTriggers) throws XMLStreamException { + // if (redirectStream != null) { + // XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); + // redirectWriter = outputFactory.createXMLEventWriter(redirectStream); + // } else { + // redirectWriter = null; + // } + // this.redirectTriggers = (redirectStream == null) ? null : redirectTriggers; + // } + /** + * flushes the internal EventWriter + * + * @throws javax.xml.stream.XMLStreamException + */ + public void flushRedirectStream() throws XMLStreamException { + redirectWriter.flush(); + } - /** - * the namespaceContext of the last startelement event read - * - * @return - */ - public NamespaceContext getCurrentNamespaceContext() { - return currentNamespaceContext; - } + /** + * the namespaceContext of the last startelement event read + * + * @return + */ + public NamespaceContext getCurrentNamespaceContext() { + return currentNamespaceContext; + } } diff --git a/utils/src/main/java/at/gv/egiz/slbinding/impl/TransformsInfoType.java b/utils/src/main/java/at/gv/egiz/slbinding/impl/TransformsInfoType.java index b4e988f0..1180e9fa 100644 --- a/utils/src/main/java/at/gv/egiz/slbinding/impl/TransformsInfoType.java +++ b/utils/src/main/java/at/gv/egiz/slbinding/impl/TransformsInfoType.java @@ -25,6 +25,7 @@ import java.io.ByteArrayOutputStream; import java.util.HashSet; import java.util.Set; import javax.xml.bind.annotation.XmlTransient; +import javax.xml.namespace.NamespaceContext; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamException; import org.apache.commons.logging.Log; diff --git a/utils/src/main/java/at/gv/egiz/slbinding/impl/XMLContentType.java b/utils/src/main/java/at/gv/egiz/slbinding/impl/XMLContentType.java index c32542aa..eb147f88 100644 --- a/utils/src/main/java/at/gv/egiz/slbinding/impl/XMLContentType.java +++ b/utils/src/main/java/at/gv/egiz/slbinding/impl/XMLContentType.java @@ -35,7 +35,7 @@ import org.apache.commons.logging.LogFactory; public class XMLContentType extends at.buergerkarte.namespaces.securitylayer._1.XMLContentType implements RedirectCallback { @XmlTransient - private static Log log = LogFactory.getLog(TransformsInfoType.class); + private static Log log = LogFactory.getLog(XMLContentType.class); @XmlTransient protected ByteArrayOutputStream redirectOS = null; diff --git a/utils/src/main/java/org/w3/_2000/_09/xmldsig_/ObjectFactory.java b/utils/src/main/java/org/w3/_2000/_09/xmldsig_/ObjectFactory.java index 4ab376f1..fae77451 100644 --- a/utils/src/main/java/org/w3/_2000/_09/xmldsig_/ObjectFactory.java +++ b/utils/src/main/java/org/w3/_2000/_09/xmldsig_/ObjectFactory.java @@ -167,6 +167,7 @@ public class ObjectFactory { * */ public TransformType createTransformType() { +// return new at.gv.egiz.slbinding.impl.TransformType(); return new TransformType(); } diff --git a/utils/src/main/java/org/w3/_2000/_09/xmldsig_/TransformsType.java b/utils/src/main/java/org/w3/_2000/_09/xmldsig_/TransformsType.java index 584baf80..c7044c4c 100644 --- a/utils/src/main/java/org/w3/_2000/_09/xmldsig_/TransformsType.java +++ b/utils/src/main/java/org/w3/_2000/_09/xmldsig_/TransformsType.java @@ -57,7 +57,7 @@ import javax.xml.bind.annotation.XmlType; }) public class TransformsType { - @XmlElement(name = "Transform", required = true) + @XmlElement(name = "Transform", required = true) //, type=at.gv.egiz.slbinding.impl.TransformType.class) protected List transform; /** diff --git a/utils/src/test/java/at/gv/egiz/slbinding/RedirectTest.java b/utils/src/test/java/at/gv/egiz/slbinding/RedirectTest.java index 99d353ac..7c8c206a 100644 --- a/utils/src/test/java/at/gv/egiz/slbinding/RedirectTest.java +++ b/utils/src/test/java/at/gv/egiz/slbinding/RedirectTest.java @@ -52,6 +52,8 @@ import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLInputFactory; import static org.junit.Assert.*; +import org.w3._2000._09.xmldsig_.TransformType; +import org.w3._2000._09.xmldsig_.TransformsType; /** * @@ -131,11 +133,34 @@ public class RedirectTest { Iterator tiIt = transformsInfos.iterator(); while (tiIt.hasNext()) { at.gv.egiz.slbinding.impl.TransformsInfoType ti = (at.gv.egiz.slbinding.impl.TransformsInfoType) tiIt.next(); +// TransformsInfoType ti = tiIt.next(); assertNotNull(ti); - System.out.println("found at.gv.egiz.slbinding.impl.TransformsInfoType TransformsInfo"); + System.out.println("found sl:TransformsInfo: " + ti.getClass().getName()); //at.gv.egiz.slbinding.impl.TransformsInfoType TransformsInfo"); +// TransformsType ts = ti.getTransforms(); +// assertNotNull(ts); +// System.out.println("found dsig:Transforms " + ts.getClass().getName()); //org.w3._2000._09.xmldsig_.TransformsType dsig:Transforms"); +// List tL = ts.getTransform(); +// assertNotNull(tL); +// System.out.println("found " + tL.size() + " org.w3._2000._09.xmldsig_.TransformType dsig:Transform"); +// for (TransformType t : tL) { +// if (t instanceof at.gv.egiz.slbinding.impl.TransformType) { +// System.out.println("found at.gv.egiz.slbinding.impl.TransformType"); +// byte[] redirectedBytes = ((at.gv.egiz.slbinding.impl.TransformType) t).getRedirectedStream().toByteArray(); +// if (redirectedBytes != null && redirectedBytes.length > 0) { +// System.out.println("reading redirected stream..."); +// os.write("--- redirected Transform ---".getBytes()); +// os.write(redirectedBytes); +// os.write("\n".getBytes()); +// } else { +// System.out.println("no redirected stream"); +// } +// } +// } + ByteArrayOutputStream dsigTransforms = ti.getRedirectedStream(); + os.write("--- redirected TransformsInfo content ---".getBytes()); os.write(dsigTransforms.toByteArray()); - os.write("\n".getBytes()); + os.write("\n---".getBytes()); MetaInfoType mi = ti.getFinalDataMetaInfo(); assertNotNull(mi); diff --git a/utils/src/test/requests/CreateXMLSignatureRequest02.xml_redirect.txt b/utils/src/test/requests/CreateXMLSignatureRequest02.xml_redirect.txt index 31be50b7..fc0e4f14 100644 --- a/utils/src/test/requests/CreateXMLSignatureRequest02.xml_redirect.txt +++ b/utils/src/test/requests/CreateXMLSignatureRequest02.xml_redirect.txt @@ -1,4 +1,4 @@ - +--- redirected TransformsInfo content --- @@ -82,7 +82,7 @@ - +------ redirected TransformsInfo content --- @@ -162,3 +162,4 @@ +--- \ No newline at end of file -- cgit v1.2.3 From bd070e82c276afb8c1c3a9ddc3b5712783760881 Mon Sep 17 00:00:00 2001 From: mcentner Date: Tue, 29 Sep 2009 17:36:06 +0000 Subject: Logging issues fixed: - Added possibility to configure logging of BKUWebstart. Logging is now configured from log4j configuration deployed with BKUWebstart in a first step. In a second step the webstart launcher looks for a log4j configuration file in the user's mooca configuration directory and updates the log4j configuration. - Logging of IAIK PKI properly initialized. IAIK PKI does not mess with the log4j configuration any longer. - Changed log4j accordingly (an appender is now needed as IAIK PKI does not reconfigure log4j any longer). Added css-stylesheet to ErrorResponses issued by the BKU to improve the presentation to the user. Changed dependencies of BKUWebStart (see Issue#469 https://egovlabs.gv.at/tracker/index.php?func=detail&aid=469&group_id=13&atid=134). DataURLConnection now uses the request encoding of SL < 1.2. application/x-www-form-urlencoded is now used as default encoding method. multipart/form-data is used only if transfer parameters are present in the request that require a Content-Type parameter. This can only be set with multipart/form-data. This is not in conformance with SL 1.2, however it should improve compatibility with applications. Therefore, removed the ability to configure the DataURLConnection implementation class. DataURLConnection now uses a streaming implementation for encoding of application/x-www-form-urlencoded requests. XWWWFormUrlImputDecoder now uses a streaming implementation for decoding of application/x-www-form-urlencoded requests. Fixed Bug in SLResultPart that caused a binary response to be provided as parameter "XMLResponse" in a multipart/form-data encoded request to DataURL. SLCommandFactory now supports unmarshalling of SL < 1.2 requests in order issue meaningful error messages. Therefore, the marshaling context for response marshaling had to be separated from the marshaling context for requests in order to avoid the marshaling of SL < 1.2 namespace prefixes in SL 1.2 responses. Target attribute in QualifiedProperties is now marshaled. (see Issue#470 https://egovlabs.gv.at/tracker/index.php?func=detail&aid=470&group_id=13&atid=134) Reporting of XML validation errors improved. git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@510 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- BKULocal/src/main/resources/log4j.properties | 2 +- BKULocal/src/main/webapp/errorresponse.css | 12 + BKUOnline/src/main/webapp/errorresponse.css | 12 + BKUWebStart/pom.xml | 14 +- .../java/at/gv/egiz/bku/webstart/Configurator.java | 26 +- .../java/at/gv/egiz/bku/webstart/Container.java | 11 +- .../java/at/gv/egiz/bku/webstart/Launcher.java | 22 +- .../gv/egiz/bku/webstart/LogSecurityManager.java | 9 +- .../java/at/gv/egiz/bku/webstart/TLSServerCA.java | 9 +- .../at/gv/egiz/bku/webstart/gui/AboutDialog.java | 7 +- .../bku/webstart/gui/PINManagementInvoker.java | 7 +- BKUWebStart/src/main/resources/log4j.properties | 20 +- .../at/gv/egiz/bku/webstart/ConfiguratorTest.java | 2 - BKUWebStartPackage/pom.xml | 3 +- .../main/java/at/gv/egiz/bku/binding/DataUrl.java | 9 +- .../at/gv/egiz/bku/binding/DataUrlConnection.java | 2 +- .../gv/egiz/bku/binding/DataUrlConnectionImpl.java | 342 ++++++- .../gv/egiz/bku/binding/HTTPBindingProcessor.java | 29 +- .../bku/binding/LegacyDataUrlConnectionImpl.java | 259 ----- .../egiz/bku/binding/XWWWFormUrlInputDecoder.java | 83 +- .../egiz/bku/binding/XWWWFormUrlInputIterator.java | 376 +++++++ .../egiz/bku/binding/multipart/SLResultPart.java | 41 +- .../at/gv/egiz/bku/conf/CertValidatorImpl.java | 24 + .../java/at/gv/egiz/bku/conf/IAIKCommonsLog.java | 144 +++ .../at/gv/egiz/bku/conf/IAIKCommonsLogFactory.java | 59 ++ .../gv/egiz/bku/slcommands/SLCommandFactory.java | 51 +- .../egiz/bku/slcommands/SLMarshallerFactory.java | 172 ++++ .../java/at/gv/egiz/bku/slcommands/SLResult.java | 8 +- .../slcommands/impl/AbstractAssocArrayInfobox.java | 13 +- .../impl/CreateXMLSignatureResultImpl.java | 15 +- .../egiz/bku/slcommands/impl/ErrorResultImpl.java | 6 +- .../bku/slcommands/impl/GetStatusCommandImpl.java | 2 - .../bku/slcommands/impl/GetStatusResultImpl.java | 4 +- .../slcommands/impl/IdentityLinkInfoboxImpl.java | 1 - .../slcommands/impl/InfoboxReadResultFileImpl.java | 15 +- .../bku/slcommands/impl/InfoboxReadResultImpl.java | 4 +- .../slcommands/impl/InfoboxUpdateResultImpl.java | 4 +- .../slcommands/impl/NullOperationResultImpl.java | 4 +- .../gv/egiz/bku/slcommands/impl/SLResultImpl.java | 94 +- .../egiz/bku/slcommands/impl/xsect/DataObject.java | 1 - .../egiz/bku/slcommands/impl/xsect/Signature.java | 18 +- .../egiz/bku/slexceptions/SLExceptionMessages.java | 6 + .../egiz/bku/slexceptions/SLVersionException.java | 28 + .../egiz/bku/slcommands/schema/Core.20020225.xsd | 33 + .../egiz/bku/slcommands/schema/Core.20020831.xsd | 10 + .../slexceptions/SLExceptionMessages.properties | 7 +- .../slexceptions/SLExceptionMessages_en.properties | 4 + .../bku/binding/XWWWFormUrlInputIteratorTest.java | 152 +++ .../egiz/bku/slcommands/SLCommandFactoryTest.java | 7 +- .../impl/CreateXMLSignatureComandImplTest.java | 9 +- .../bku/slcommands/impl/ErrorResultImplTest.java | 2 +- .../slcommands/impl/InfoboxReadComandImplTest.java | 9 +- .../impl/NullOperationResultImplTest.java | 2 +- .../impl/SVPersonendatenInfoboxImplTest.java | 15 +- .../securitylayer/_1/TransformsInfoType.java | 21 +- .../_20020225_/ErrorResponseType.java | 98 ++ .../securitylayer/_20020225_/ObjectFactory.java | 280 ++++++ .../securitylayer/_20020225_/package-info.java | 9 + .../securitylayer/_20020831_/ObjectFactory.java | 112 +++ .../gv/egiz/bku/utils/URLEncodingInputStream.java | 62 ++ .../gv/egiz/bku/utils/URLEncodingOutputStream.java | 134 +++ .../at/gv/egiz/bku/utils/URLEncodingWriter.java | 57 ++ .../java/at/gv/egiz/marshal/MarshallerFactory.java | 12 +- .../java/at/gv/egiz/marshal/NamespacePrefix.java | 34 - .../gv/egiz/marshal/NamespacePrefixMapperImpl.java | 54 +- .../ReportingValidationEventHandler.java | 64 ++ .../gv/egiz/validation/ValidationEventLogger.java | 55 - .../gv/egiz/xades/QualifyingPropertiesFactory.java | 8 +- .../bku/utils/URLEncodingOutputStreamTest.java | 147 +++ utils/src/test/resources/BigRequest.xml | 1060 ++++++++++++++++++++ 70 files changed, 3704 insertions(+), 723 deletions(-) create mode 100644 BKULocal/src/main/webapp/errorresponse.css create mode 100644 BKUOnline/src/main/webapp/errorresponse.css delete mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/binding/LegacyDataUrlConnectionImpl.java create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputIterator.java create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/conf/IAIKCommonsLog.java create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/conf/IAIKCommonsLogFactory.java create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLMarshallerFactory.java create mode 100644 bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLVersionException.java create mode 100644 bkucommon/src/main/resources/at/gv/egiz/bku/slcommands/schema/Core.20020225.xsd create mode 100644 bkucommon/src/main/resources/at/gv/egiz/bku/slcommands/schema/Core.20020831.xsd create mode 100644 bkucommon/src/test/java/at/gv/egiz/bku/binding/XWWWFormUrlInputIteratorTest.java create mode 100644 utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_20020225_/ErrorResponseType.java create mode 100644 utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_20020225_/ObjectFactory.java create mode 100644 utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_20020225_/package-info.java create mode 100644 utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_20020831_/ObjectFactory.java create mode 100644 utils/src/main/java/at/gv/egiz/bku/utils/URLEncodingInputStream.java create mode 100644 utils/src/main/java/at/gv/egiz/bku/utils/URLEncodingOutputStream.java create mode 100644 utils/src/main/java/at/gv/egiz/bku/utils/URLEncodingWriter.java delete mode 100644 utils/src/main/java/at/gv/egiz/marshal/NamespacePrefix.java create mode 100644 utils/src/main/java/at/gv/egiz/validation/ReportingValidationEventHandler.java delete mode 100644 utils/src/main/java/at/gv/egiz/validation/ValidationEventLogger.java create mode 100644 utils/src/test/java/at/gv/egiz/bku/utils/URLEncodingOutputStreamTest.java create mode 100644 utils/src/test/resources/BigRequest.xml (limited to 'utils/src/main/java/at/gv/egiz/marshal/NamespacePrefixMapperImpl.java') diff --git a/BKULocal/src/main/resources/log4j.properties b/BKULocal/src/main/resources/log4j.properties index 8dc8644c..86ddc7b4 100644 --- a/BKULocal/src/main/resources/log4j.properties +++ b/BKULocal/src/main/resources/log4j.properties @@ -15,7 +15,7 @@ # assume log4j to be configured by servlet container (java web start) # loglever DEBUG, appender STDOUT -#log4j.rootLogger=DEBUG, STDOUT +log4j.rootLogger=DEBUG, STDOUT # STDOUT appender log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender diff --git a/BKULocal/src/main/webapp/errorresponse.css b/BKULocal/src/main/webapp/errorresponse.css new file mode 100644 index 00000000..41402e71 --- /dev/null +++ b/BKULocal/src/main/webapp/errorresponse.css @@ -0,0 +1,12 @@ +@CHARSET "UTF-8"; +sl\:ErrorResponse {margin: 0.5em; display: block;} +sl\:ErrorCode {display: inline;} +sl\:Info {display: inline;} + +ErrorResponse:lang(de):before {content: "Bei der Verarbeitung der Anfrage durch die Bürgerkartenumgebung ist ein Fehler aufgetreten: "; font-weight: bolder;} +ErrorResponse:before {content: "An error has occoured upon request processing by the citizen card software: "; font-weight: bold;} +ErrorResponse {margin: 0.5em; display: block;} +ErrorCode:lang(de):before {content: "Fehler-Code: ";} +ErrorCode:before {content: "Error Code: ";} +ErrorCode {display: block;} +Info {display: block;} \ No newline at end of file diff --git a/BKUOnline/src/main/webapp/errorresponse.css b/BKUOnline/src/main/webapp/errorresponse.css new file mode 100644 index 00000000..41402e71 --- /dev/null +++ b/BKUOnline/src/main/webapp/errorresponse.css @@ -0,0 +1,12 @@ +@CHARSET "UTF-8"; +sl\:ErrorResponse {margin: 0.5em; display: block;} +sl\:ErrorCode {display: inline;} +sl\:Info {display: inline;} + +ErrorResponse:lang(de):before {content: "Bei der Verarbeitung der Anfrage durch die Bürgerkartenumgebung ist ein Fehler aufgetreten: "; font-weight: bolder;} +ErrorResponse:before {content: "An error has occoured upon request processing by the citizen card software: "; font-weight: bold;} +ErrorResponse {margin: 0.5em; display: block;} +ErrorCode:lang(de):before {content: "Fehler-Code: ";} +ErrorCode:before {content: "Error Code: ";} +ErrorCode {display: block;} +Info {display: block;} \ No newline at end of file diff --git a/BKUWebStart/pom.xml b/BKUWebStart/pom.xml index ca19a0b3..f51f1332 100644 --- a/BKUWebStart/pom.xml +++ b/BKUWebStart/pom.xml @@ -172,6 +172,12 @@ BKUCertificates 1.0 + + iaik + iaik_jce_full_signed + compile + + + @@ -215,6 +222,11 @@ slf4j-log4j12 1.5.8 + + log4j + log4j + compile + diff --git a/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/Configurator.java b/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/Configurator.java index 923a70d9..d8fe3e70 100644 --- a/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/Configurator.java +++ b/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/Configurator.java @@ -16,8 +16,9 @@ */ package at.gv.egiz.bku.webstart; -import at.gv.egiz.bku.utils.StreamUtil; import iaik.asn1.CodingException; +import iaik.utils.StreamCopier; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; @@ -42,8 +43,10 @@ import java.util.jar.Manifest; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; + +import org.apache.log4j.PropertyConfigurator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @@ -71,7 +74,7 @@ public class Configurator { public static final String KEYSTORE_FILE = "keystore.ks"; public static final String PASSWD_FILE = ".secret"; - private static final Log log = LogFactory.getLog(Configurator.class); + private static final Logger log = LoggerFactory.getLogger(Configurator.class); /** currently installed configuration version */ private String version; @@ -110,6 +113,11 @@ public class Configurator { } else { initConfig(configDir); } + // re-configure logging + // TODO: move to appropriate place + String log4jconfig = configDir.getPath() + File.separatorChar + "log4j.properties"; + log.debug("Reconfiguring logging with " + log4jconfig); + PropertyConfigurator.configureAndWatch(log4jconfig); } /** @@ -312,7 +320,7 @@ public class Configurator { ZipEntry entry = new ZipEntry(relativePath.toString()); zip.putNextEntry(entry); BufferedInputStream entryIS = new BufferedInputStream(new FileInputStream(dir)); - StreamUtil.copyStream(entryIS, zip); + new StreamCopier(entryIS, zip).copyStream(); entryIS.close(); zip.closeEntry(); dir.delete(); @@ -341,7 +349,7 @@ public class Configurator { File confTemplateFile = new File(configDir, CONF_TEMPLATE_FILE); InputStream is = Configurator.class.getClassLoader().getResourceAsStream(CONF_TEMPLATE_RESOURCE); OutputStream os = new BufferedOutputStream(new FileOutputStream(confTemplateFile)); - StreamUtil.copyStream(is, os); + new StreamCopier(is, os).copyStream(); os.close(); unzip(confTemplateFile, configDir); confTemplateFile.delete(); @@ -374,7 +382,7 @@ public class Configurator { new File(certsDir, f.substring(0, f.lastIndexOf('/'))).mkdirs(); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(certsDir, f))); log.debug(f); - StreamUtil.copyStream(Configurator.class.getClassLoader().getResourceAsStream(entry), bos); + new StreamCopier(Configurator.class.getClassLoader().getResourceAsStream(entry), bos).copyStream(); bos.close(); } else { log.trace("ignore " + entry); @@ -399,8 +407,8 @@ public class Configurator { } File f = new File(eF.getParent()); f.mkdirs(); - StreamUtil.copyStream(zipFile.getInputStream(entry), - new FileOutputStream(eF)); + new StreamCopier(zipFile.getInputStream(entry), + new FileOutputStream(eF)).copyStream(); } zipFile.close(); } diff --git a/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/Container.java b/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/Container.java index 2feae267..4d1fe658 100644 --- a/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/Container.java +++ b/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/Container.java @@ -1,6 +1,7 @@ package at.gv.egiz.bku.webstart; -import at.gv.egiz.bku.utils.StreamUtil; +import iaik.utils.StreamCopier; + import java.awt.AWTPermission; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -24,20 +25,20 @@ import java.security.SecurityPermission; import java.security.cert.Certificate; import java.util.PropertyPermission; import javax.smartcardio.CardPermission; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.mortbay.jetty.Connector; import org.mortbay.jetty.Server; import org.mortbay.jetty.nio.SelectChannelConnector; import org.mortbay.jetty.security.SslSocketConnector; import org.mortbay.jetty.webapp.WebAppContext; import org.mortbay.thread.QueuedThreadPool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Container { public static final String HTTP_PORT_PROPERTY = "mocca.http.port"; public static final String HTTPS_PORT_PROPERTY = "mocca.http.port"; - private static Log log = LogFactory.getLog(Container.class); + private static Logger log = LoggerFactory.getLogger(Container.class); static { if (log.isDebugEnabled()) { @@ -166,7 +167,7 @@ public class Container { log.debug("copying BKULocal classpath resource to " + webapp); InputStream is = getClass().getClassLoader().getResourceAsStream("BKULocal.war"); OutputStream os = new BufferedOutputStream(new FileOutputStream(webapp)); - StreamUtil.copyStream(is, os); + new StreamCopier(is, os).copyStream(); os.close(); return webapp.getPath(); } diff --git a/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/Launcher.java b/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/Launcher.java index 2bf42ccb..ef7edef1 100644 --- a/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/Launcher.java +++ b/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/Launcher.java @@ -10,8 +10,6 @@ import java.util.Locale; import java.util.ResourceBundle; import javax.jnlp.UnavailableServiceException; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import com.sun.javaws.security.JavaWebStartSecurity; import java.awt.AWTException; @@ -37,6 +35,8 @@ import javax.jnlp.BasicService; import javax.jnlp.ServiceManager; import javax.swing.JFrame; import org.mortbay.util.MultiException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Launcher implements BKUControllerInterface, ActionListener { public static final String HELP_COMMAND = "help"; @@ -71,9 +71,10 @@ public class Launcher implements BKUControllerInterface, ActionListener { public static final String SHUTDOWN_COMMAND = "shutdown"; public static final String PIN_COMMAND = "pin"; public static final String ABOUT_COMMAND = "about"; + + private static Logger log = LoggerFactory.getLogger(Launcher.class); - private static Log log = LogFactory.getLog(Launcher.class); - + /** local bku uri */ public static final URL HTTP_SECURITY_LAYER_URL; public static final URL HTTPS_SECURITY_LAYER_URL; @@ -93,7 +94,7 @@ public class Launcher implements BKUControllerInterface, ActionListener { cert = new URL(http, "/installCertificate"); help = new URL(http, "/help"); } catch (MalformedURLException ex) { - log.error(ex); + log.error("Failed to create URL.", ex); } finally { HTTP_SECURITY_LAYER_URL = http; HTTPS_SECURITY_LAYER_URL = https; @@ -132,6 +133,7 @@ public class Launcher implements BKUControllerInterface, ActionListener { public Launcher() { + log.info("Initializing Launcher"); if (log.isTraceEnabled()) { SecurityManager sm = System.getSecurityManager(); if (sm instanceof JavaWebStartSecurity) { @@ -147,7 +149,7 @@ public class Launcher implements BKUControllerInterface, ActionListener { try { initConfig(); } catch (Exception ex) { - log.fatal("Failed to initialize configuration", ex); + log.error("Failed to initialize configuration", ex); trayIcon.displayMessage(messages.getString(CAPTION_ERROR), messages.getString(ERROR_CONFIG), TrayIcon.MessageType.ERROR); throw ex; @@ -156,12 +158,12 @@ public class Launcher implements BKUControllerInterface, ActionListener { startServer(); initFinished(); } catch (BindException ex) { - log.fatal("Failed to launch server, " + ex.getMessage(), ex); + log.error("Failed to launch server, " + ex.getMessage(), ex); trayIcon.displayMessage(messages.getString(CAPTION_ERROR), messages.getString(ERROR_BIND), TrayIcon.MessageType.ERROR); throw ex; } catch (MultiException ex) { - log.fatal("Failed to launch server, " + ex.getMessage(), ex); + log.error("Failed to launch server, " + ex.getMessage(), ex); if (ex.getThrowable(0) instanceof BindException) { trayIcon.displayMessage(messages.getString(CAPTION_ERROR), messages.getString(ERROR_BIND), TrayIcon.MessageType.ERROR); @@ -172,7 +174,7 @@ public class Launcher implements BKUControllerInterface, ActionListener { throw ex; } catch (Exception ex) { ex.printStackTrace(); - log.fatal("Failed to launch server, " + ex.getMessage(), ex); + log.error("Failed to launch server, " + ex.getMessage(), ex); trayIcon.displayMessage(messages.getString(CAPTION_ERROR), messages.getString(ERROR_START), TrayIcon.MessageType.ERROR); throw ex; @@ -379,7 +381,7 @@ public class Launcher implements BKUControllerInterface, ActionListener { launcher.launch(); } catch (Exception ex) { ex.printStackTrace(); - log.debug(ex); + log.debug("Caught exception " + ex.getMessage(), ex); log.info("waiting to shutdown..."); Thread.sleep(5000); log.info("exit"); diff --git a/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/LogSecurityManager.java b/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/LogSecurityManager.java index 99fd403b..d589812e 100644 --- a/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/LogSecurityManager.java +++ b/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/LogSecurityManager.java @@ -20,8 +20,9 @@ import com.sun.javaws.security.JavaWebStartSecurity; import java.io.FileDescriptor; import java.net.InetAddress; import java.security.Permission; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * JVM argument -Djava.security.debug=access,failure @@ -31,7 +32,7 @@ import org.apache.commons.logging.LogFactory; */ public class LogSecurityManager extends SecurityManager { - protected static final Log log = LogFactory.getLog(LogSecurityManager.class); + protected static final Logger log = LoggerFactory.getLogger(LogSecurityManager.class); JavaWebStartSecurity sm; public LogSecurityManager(JavaWebStartSecurity sm) { @@ -182,6 +183,7 @@ public class LogSecurityManager extends SecurityManager { } } + @SuppressWarnings("deprecation") @Override public void checkMulticast(InetAddress maddr, byte ttl) { try { @@ -399,6 +401,7 @@ public class LogSecurityManager extends SecurityManager { // protected Class[] getClassContext() { // log.info("getClassContext"); return sm.getClassContext(); // } + @SuppressWarnings("deprecation") @Override public boolean getInCheck() { log.info("getInCheck"); diff --git a/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/TLSServerCA.java b/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/TLSServerCA.java index 08a06570..745042f8 100644 --- a/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/TLSServerCA.java +++ b/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/TLSServerCA.java @@ -16,8 +16,6 @@ import iaik.x509.extensions.SubjectAltName; import iaik.x509.extensions.SubjectKeyIdentifier; import java.io.IOException; import java.math.BigInteger; -import java.net.InetAddress; -import java.net.UnknownHostException; import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.KeyPairGenerator; @@ -27,14 +25,15 @@ import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Random; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class TLSServerCA { public static final int CA_VALIDITY_Y = 3; public static final String MOCCA_TLS_SERVER_ALIAS = "server"; public static final int SERVER_VALIDITY_Y = 3; - private final static Log log = LogFactory.getLog(TLSServerCA.class); + private final static Logger log = LoggerFactory.getLogger(TLSServerCA.class); private KeyPair caKeyPair; private X509Certificate caCert; diff --git a/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/gui/AboutDialog.java b/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/gui/AboutDialog.java index 1e35af58..ba2c007d 100644 --- a/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/gui/AboutDialog.java +++ b/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/gui/AboutDialog.java @@ -11,7 +11,6 @@ package at.gv.egiz.bku.webstart.gui; -import java.text.Format; import java.text.MessageFormat; import java.util.ResourceBundle; @@ -21,6 +20,11 @@ import java.util.ResourceBundle; */ public class AboutDialog extends javax.swing.JDialog { + /** + * + */ + private static final long serialVersionUID = 1L; + /** Creates new form AboutDialog */ public AboutDialog(java.awt.Frame parent, boolean modal, String version) { super(parent, modal); @@ -33,7 +37,6 @@ public class AboutDialog extends javax.swing.JDialog { * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ - @SuppressWarnings("unchecked") // //GEN-BEGIN:initComponents private void initComponents() { diff --git a/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/gui/PINManagementInvoker.java b/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/gui/PINManagementInvoker.java index 55e26313..1f14d751 100644 --- a/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/gui/PINManagementInvoker.java +++ b/BKUWebStart/src/main/java/at/gv/egiz/bku/webstart/gui/PINManagementInvoker.java @@ -21,8 +21,9 @@ import java.awt.TrayIcon; import java.io.IOException; import java.net.HttpURLConnection; import java.util.ResourceBundle; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * GUI is painted using SwingUtilities.invokeLater, but TrayIcon ActionListener Thread (== webstart thread) joined Jetty Thread @@ -31,7 +32,7 @@ import org.apache.commons.logging.LogFactory; */ public class PINManagementInvoker implements Runnable { - private static final Log log = LogFactory.getLog(PINManagementInvoker.class); + private static final Logger log = LoggerFactory.getLogger(PINManagementInvoker.class); TrayIcon trayIcon; ResourceBundle messages; diff --git a/BKUWebStart/src/main/resources/log4j.properties b/BKUWebStart/src/main/resources/log4j.properties index 76562ccf..81832418 100644 --- a/BKUWebStart/src/main/resources/log4j.properties +++ b/BKUWebStart/src/main/resources/log4j.properties @@ -13,23 +13,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -# loglever DEBUG, appender STDOUT -log4j.rootLogger=DEBUG, file -log4j.logger.org.mortbay.log=INFO -log4j.logger.pki=INFO - -#log4j.additivity.pki=false +# root log level INFO, appender file +log4j.rootLogger=INFO, file -# STDOUT appender -log4j.appender.STDOUT=org.apache.log4j.ConsoleAppender -log4j.appender.STDOUT.layout=org.apache.log4j.PatternLayout -#log4j.appender.STDOUT.layout.ConversionPattern=%5p | %d{dd HH:mm:ss,SSS} | %20c | %10t | %m%n -#log4j.appender.STDOUT.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n -log4j.appender.STDOUT.layout.ConversionPattern=%-5p |%d | %t | %c %x- %m%n +# jetty's log level +log4j.logger.org.mortbay.log=INFO -### FILE appender +# file appender log4j.appender.file=org.apache.log4j.DailyRollingFileAppender log4j.appender.file.datePattern='.'yyyy-MM-dd log4j.appender.file.File=${user.home}/.mocca/logs/webstart.log log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %-5p %c{1}:%L - %m%n \ No newline at end of file +log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %-5p %c{2} - %m%n \ No newline at end of file diff --git a/BKUWebStart/src/test/java/at/gv/egiz/bku/webstart/ConfiguratorTest.java b/BKUWebStart/src/test/java/at/gv/egiz/bku/webstart/ConfiguratorTest.java index 0ea126cb..4f5798d5 100644 --- a/BKUWebStart/src/test/java/at/gv/egiz/bku/webstart/ConfiguratorTest.java +++ b/BKUWebStart/src/test/java/at/gv/egiz/bku/webstart/ConfiguratorTest.java @@ -8,8 +8,6 @@ package at.gv.egiz.bku.webstart; import java.io.File; import java.net.URI; import java.util.zip.ZipOutputStream; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; diff --git a/BKUWebStartPackage/pom.xml b/BKUWebStartPackage/pom.xml index 63725fc3..0b226785 100644 --- a/BKUWebStartPackage/pom.xml +++ b/BKUWebStartPackage/pom.xml @@ -1,3 +1,4 @@ + 4.0.0 @@ -22,7 +23,7 @@ process-resources - jnlp-download-servlet + jnlp-single diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrl.java index 1db8c836..d3945253 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrl.java @@ -16,7 +16,6 @@ */ package at.gv.egiz.bku.binding; -import at.gv.egiz.bku.conf.Configuration; import at.gv.egiz.bku.conf.Configurator; import java.net.MalformedURLException; import java.net.URL; @@ -89,13 +88,7 @@ public class DataUrl { if (configuration != null) { String className = configuration.getProperty(Configurator.DATAURLCONNECTION_CONFIG_P); if (className != null) { - try { - log.info("set DataURLConnection class: " + className); - Class c = Class.forName(className); - connection = (DataUrlConnectionSPI) c.newInstance(); - } catch (Exception ex) { - log.error("failed to instantiate DataURL connection " + className, ex); - } + log.warn("Set DataURLConnection class not supported!"); } } } diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnection.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnection.java index f954a017..384cf71c 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnection.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnection.java @@ -62,7 +62,7 @@ public interface DataUrlConnection { * @param transferEncoding may be null */ public void setHTTPFormParameter(String name, InputStream data, String contentType, String charSet, String transferEncoding); - + /** * @pre httpHeaders != null * @throws java.net.SocketTimeoutException diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java index 4f2d2e00..b092ba41 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/DataUrlConnectionImpl.java @@ -18,10 +18,14 @@ package at.gv.egiz.bku.binding; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.net.HttpURLConnection; import java.net.SocketTimeoutException; import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.Charset; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.HashMap; @@ -34,6 +38,7 @@ import java.util.Set; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; +import javax.xml.transform.stream.StreamResult; import org.apache.commons.httpclient.methods.multipart.FilePart; import org.apache.commons.httpclient.methods.multipart.Part; @@ -47,32 +52,92 @@ import at.gv.egiz.bku.conf.Configurator; import at.gv.egiz.bku.slcommands.SLResult; import at.gv.egiz.bku.slcommands.SLResult.SLResultType; import at.gv.egiz.bku.slexceptions.SLRuntimeException; +import at.gv.egiz.bku.utils.URLEncodingWriter; import at.gv.egiz.bku.utils.binding.Protocol; /** - * not thread-safe thus newInsance always returns a new object + * An implementation of the DataUrlConnectionSPI that supports + * multipart/form-data encoding and + * application/x-www-form-urlencoded for compatibility with legacy + * systems. * */ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { private final static Log log = LogFactory.getLog(DataUrlConnectionImpl.class); + + public static final byte[] B_DEFAULT_RESPONSETYPE = DEFAULT_RESPONSETYPE.getBytes(Charset.forName("UTF-8")); + /** + * Supported protocols are HTTP and HTTPS. + */ public final static Protocol[] SUPPORTED_PROTOCOLS = { Protocol.HTTP, Protocol.HTTPS }; + /** + * The X509 certificate of the DataURL server. + */ protected X509Certificate serverCertificate; + + /** + * The protocol of the DataURL. + */ protected Protocol protocol; + + /** + * Use application/x-www-form-urlencoded instead of + * standard conform application/x-www-form-urlencoded. + */ + protected boolean urlEncoded = true; + + /** + * The value of the DataURL. + */ protected URL url; + + /** + * The URLConnection used for communication with the DataURL server. + */ private HttpURLConnection connection; + + /** + * The HTTP request headers. + */ protected Map requestHttpHeaders; - protected ArrayList formParams; + + /** + * The HTTP form parameters. + */ + protected ArrayList httpFormParameter; + + /** + * The boundary for multipart/form-data requests. + */ protected String boundary; + + /** + * The configuration properties. + */ protected Properties config = null; + + /** + * The SSLSocketFactory for HTTPS connections. + */ protected SSLSocketFactory sslSocketFactory; + + /** + * The HostnameVerifier for HTTPS connections. + */ protected HostnameVerifier hostnameVerifier; + /** + * The response of the DataURL server. + */ protected DataUrlResponse result; + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.DataUrlConnection#getProtocol() + */ public String getProtocol() { if (protocol == null) { return null; @@ -80,13 +145,8 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { return protocol.toString(); } - /** - * opens a connection sets the headers gets the server certificate - * - * @throws java.net.SocketTimeoutException - * @throws java.io.IOException - * @pre url != null - * @pre httpHeaders != null + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.DataUrlConnection#connect() */ public void connect() throws SocketTimeoutException, IOException { connection = (HttpURLConnection) url.openConnection(); @@ -104,9 +164,26 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { https.setHostnameVerifier(hostnameVerifier); } } else { - log.trace("No secure connection with: "+url+ " class="+connection.getClass()); + log.trace("No secure connection with: " + url + " class=" + + connection.getClass()); } connection.setDoOutput(true); + // Transfer-Encoding: chunked is problematic ... + // e.g. https://issues.apache.org/bugzilla/show_bug.cgi?id=37794 + // ... therefore disabled. + // connection.setChunkedStreamingMode(5*1024); + if (urlEncoded) { + log.debug("Setting DataURL Content-Type to " + + HttpUtil.APPLICATION_URL_ENCODED); + connection.addRequestProperty(HttpUtil.HTTP_HEADER_CONTENT_TYPE, + HttpUtil.APPLICATION_URL_ENCODED); + } else { + log.debug("Setting DataURL Content-Type to " + + HttpUtil.MULTIPART_FOTMDATA_BOUNDARY); + connection.addRequestProperty(HttpUtil.HTTP_HEADER_CONTENT_TYPE, + HttpUtil.MULTIPART_FOTMDATA + HttpUtil.SEPERATOR[0] + + HttpUtil.MULTIPART_FOTMDATA_BOUNDARY + "=" + boundary); + } Set headers = requestHttpHeaders.keySet(); Iterator headerIt = headers.iterator(); while (headerIt.hasNext()) { @@ -125,51 +202,128 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { } } + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.DataUrlConnection#getServerCertificate() + */ public X509Certificate getServerCertificate() { return serverCertificate; } + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.DataUrlConnection#setHTTPHeader(java.lang.String, java.lang.String) + */ public void setHTTPHeader(String name, String value) { if (name != null && value != null) { requestHttpHeaders.put(name, value); } } + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.DataUrlConnection#setHTTPFormParameter(java.lang.String, java.io.InputStream, java.lang.String, java.lang.String, java.lang.String) + */ public void setHTTPFormParameter(String name, InputStream data, String contentType, String charSet, String transferEncoding) { - InputStreamPartSource source = new InputStreamPartSource(null, data); - FilePart formParam = new FilePart(name, source, contentType, charSet); - if (transferEncoding != null) { - formParam.setTransferEncoding(transferEncoding); - } else { - formParam.setTransferEncoding(null); + // if a content type is specified we have to switch to multipart/formdata encoding + if (contentType != null && contentType.length() > 0) { + urlEncoded = false; } - formParams.add(formParam); + httpFormParameter.add(new HTTPFormParameter(name, data, contentType, + charSet, transferEncoding)); } - /** - * send all formParameters - * - * @throws java.io.IOException + + + /* (non-Javadoc) + * @see at.gv.egiz.bku.binding.DataUrlConnection#transmit(at.gv.egiz.bku.slcommands.SLResult) */ public void transmit(SLResult slResult) throws IOException { - SLResultPart slResultPart = new SLResultPart(slResult, - XML_RESPONSE_ENCODING); - if (slResult.getResultType() == SLResultType.XML) { - slResultPart.setTransferEncoding(null); - slResultPart.setContentType(slResult.getMimeType()); - slResultPart.setCharSet(XML_RESPONSE_ENCODING); + log.trace("Sending data"); + if (urlEncoded) { + // + // application/x-www-form-urlencoded (legacy, SL < 1.2) + // + + OutputStream os = connection.getOutputStream(); + OutputStreamWriter streamWriter = new OutputStreamWriter(os, HttpUtil.DEFAULT_CHARSET); + + // ResponseType + streamWriter.write(FORMPARAM_RESPONSETYPE); + streamWriter.write("="); + streamWriter.write(URLEncoder.encode(DEFAULT_RESPONSETYPE, "UTF-8")); + streamWriter.write("&"); + + // XMLResponse / Binary Response + if (slResult.getResultType() == SLResultType.XML) { + streamWriter.write(DataUrlConnection.FORMPARAM_XMLRESPONSE); + } else { + streamWriter.write(DataUrlConnection.FORMPARAM_BINARYRESPONSE); + } + streamWriter.write("="); + streamWriter.flush(); + URLEncodingWriter urlEnc = new URLEncodingWriter(streamWriter); + slResult.writeTo(new StreamResult(urlEnc), false); + urlEnc.flush(); + + // transfer parameters + char[] cbuf = new char[512]; + int len; + for (HTTPFormParameter formParameter : httpFormParameter) { + streamWriter.write("&"); + streamWriter.write(URLEncoder.encode(formParameter.getName(), "UTF-8")); + streamWriter.write("="); + InputStreamReader reader = new InputStreamReader(formParameter.getData(), + (formParameter.getCharSet() != null) + ? formParameter.getCharSet() + : null); + while ((len = reader.read(cbuf)) != -1) { + urlEnc.write(cbuf, 0, len); + } + urlEnc.flush(); + } + streamWriter.close(); + } else { - slResultPart.setTransferEncoding(null); - slResultPart.setContentType(slResult.getMimeType()); - } - formParams.add(slResultPart); + // + // multipart/form-data (conforming to SL 1.2) + // - OutputStream os = connection.getOutputStream(); - log.trace("Sending data"); - Part[] parts = new Part[formParams.size()]; - Part.sendParts(os, formParams.toArray(parts), boundary.getBytes()); - os.close(); + ArrayList parts = new ArrayList(); + + // ResponseType + StringPart responseType = new StringPart(FORMPARAM_RESPONSETYPE, + DEFAULT_RESPONSETYPE, "UTF-8"); + responseType.setTransferEncoding(null); + parts.add(responseType); + + // XMLResponse / Binary Response + SLResultPart slResultPart = new SLResultPart(slResult, + XML_RESPONSE_ENCODING); + if (slResult.getResultType() == SLResultType.XML) { + slResultPart.setTransferEncoding(null); + slResultPart.setContentType(slResult.getMimeType()); + slResultPart.setCharSet(XML_RESPONSE_ENCODING); + } else { + slResultPart.setTransferEncoding(null); + slResultPart.setContentType(slResult.getMimeType()); + } + parts.add(slResultPart); + + // transfer parameters + for (HTTPFormParameter formParameter : httpFormParameter) { + InputStreamPartSource source = new InputStreamPartSource(null, + formParameter.getData()); + FilePart part = new FilePart(formParameter.getName(), source, + formParameter.getContentType(), formParameter.getCharSet()); + part.setTransferEncoding(formParameter.getTransferEncoding()); + parts.add(part); + } + + OutputStream os = connection.getOutputStream(); + Part.sendParts(os, parts.toArray(new Part[parts.size()]), boundary.getBytes()); + os.close(); + + } + // MultipartRequestEntity PostMethod InputStream is = null; try { @@ -241,16 +395,9 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { .put(HttpUtil.HTTP_HEADER_USER_AGENT, Configurator.USERAGENT_DEFAULT); } - requestHttpHeaders.put(HttpUtil.HTTP_HEADER_CONTENT_TYPE, - HttpUtil.MULTIPART_FOTMDATA + HttpUtil.SEPERATOR[0] - + HttpUtil.MULTIPART_FOTMDATA_BOUNDARY + "=" + boundary); - - formParams = new ArrayList(); - StringPart responseType = new StringPart(FORMPARAM_RESPONSETYPE, - DEFAULT_RESPONSETYPE); - responseType.setCharSet("UTF-8"); - responseType.setTransferEncoding(null); - formParams.add(responseType); + + httpFormParameter = new ArrayList(); + } @Override @@ -281,4 +428,107 @@ public class DataUrlConnectionImpl implements DataUrlConnectionSPI { public void setHostnameVerifier(HostnameVerifier hostnameVerifier) { this.hostnameVerifier = hostnameVerifier; } + + public class HTTPFormParameter { + + private String name; + + private InputStream data; + + private String contentType; + + private String charSet; + + private String transferEncoding; + + /** + * @param name + * @param data + * @param contentType + * @param charSet + * @param transferEncoding + */ + public HTTPFormParameter(String name, InputStream data, String contentType, + String charSet, String transferEncoding) { + super(); + this.name = name; + this.data = data; + this.contentType = contentType; + this.charSet = charSet; + this.transferEncoding = transferEncoding; + } + + /** + * @return the name + */ + public String getName() { + return name; + } + + /** + * @param name the name to set + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the data + */ + public InputStream getData() { + return data; + } + + /** + * @param data the data to set + */ + public void setData(InputStream data) { + this.data = data; + } + + /** + * @return the contentType + */ + public String getContentType() { + return contentType; + } + + /** + * @param contentType the contentType to set + */ + public void setContentType(String contentType) { + this.contentType = contentType; + } + + /** + * @return the charSet + */ + public String getCharSet() { + return charSet; + } + + /** + * @param charSet the charSet to set + */ + public void setCharSet(String charSet) { + this.charSet = charSet; + } + + /** + * @return the transferEncoding + */ + public String getTransferEncoding() { + return transferEncoding; + } + + /** + * @param transferEncoding the transferEncoding to set + */ + public void setTransferEncoding(String transferEncoding) { + this.transferEncoding = transferEncoding; + } + + + + } } \ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java index ef603fc7..a1c4d5fc 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/HTTPBindingProcessor.java @@ -22,6 +22,7 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Reader; +import java.io.Writer; import java.net.URL; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -46,6 +47,7 @@ import javax.xml.transform.stream.StreamSource; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import at.gv.egiz.bku.slcommands.ErrorResult; import at.gv.egiz.bku.slcommands.SLCommand; import at.gv.egiz.bku.slcommands.SLCommandContext; import at.gv.egiz.bku.slcommands.SLCommandFactory; @@ -635,7 +637,6 @@ public class HTTPBindingProcessor extends AbstractBindingProcessor implements throw new SLBindingException(2006); } InputDecoder id = InputDecoderFactory.getDecoder(cl, is); - id.setContentType(cl); if (id == null) { log.error("Cannot get inputdecoder for is"); throw new SLException(2006); @@ -730,9 +731,20 @@ public class HTTPBindingProcessor extends AbstractBindingProcessor implements Templates templates) throws IOException { log.debug("Writing error as result"); ErrorResultImpl error = new ErrorResultImpl(bindingProcessorError, locale); - error.writeTo(new StreamResult(new OutputStreamWriter(os, encoding)), templates); + Writer writer = writeXMLDeclarationAndProcessingInstruction(os, encoding); + error.writeTo(new StreamResult(writer), templates, true); } + protected Writer writeXMLDeclarationAndProcessingInstruction(OutputStream os, String encoding) throws IOException { + if (encoding == null) { + encoding = HttpUtil.DEFAULT_CHARSET; + } + OutputStreamWriter writer = new OutputStreamWriter(os, encoding); + writer.write("\n"); + writer.write("\n"); + return writer; + } + @Override public void writeResultTo(OutputStream os, String encoding) throws IOException { @@ -772,9 +784,16 @@ public class HTTPBindingProcessor extends AbstractBindingProcessor implements return; } else { log.debug("Getting result from invoker"); - OutputStreamWriter osw = new OutputStreamWriter(os, encoding); - slResult.writeTo(new StreamResult(osw), templates); - osw.flush(); + boolean fragment = false; + Writer writer; + if (slResult instanceof ErrorResult) { + writer = writeXMLDeclarationAndProcessingInstruction(os, encoding); + fragment = true; + } else { + writer = new OutputStreamWriter(os, encoding); + } + slResult.writeTo(new StreamResult(writer), templates, fragment); + writer.flush(); } } diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/LegacyDataUrlConnectionImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/LegacyDataUrlConnectionImpl.java deleted file mode 100644 index cfccb7f1..00000000 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/LegacyDataUrlConnectionImpl.java +++ /dev/null @@ -1,259 +0,0 @@ -package at.gv.egiz.bku.binding; - - -import at.gv.egiz.bku.conf.Configurator; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.StringWriter; -import java.net.HttpURLConnection; -import java.net.SocketTimeoutException; -import java.net.URL; -import java.net.URLEncoder; -import java.security.cert.X509Certificate; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; - -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLSocketFactory; -import javax.xml.transform.stream.StreamResult; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import at.gv.egiz.bku.slcommands.SLResult; -import at.gv.egiz.bku.slcommands.SLResult.SLResultType; -import at.gv.egiz.bku.slexceptions.SLRuntimeException; -import at.gv.egiz.bku.utils.binding.Protocol; - -/** - * not thread-safe thus newInsance always returns a new object - * - */ -public class LegacyDataUrlConnectionImpl implements DataUrlConnectionSPI { - - private final static Log log = LogFactory.getLog(LegacyDataUrlConnectionImpl.class); - - public final static Protocol[] SUPPORTED_PROTOCOLS = { Protocol.HTTP, - Protocol.HTTPS }; - protected X509Certificate serverCertificate; - protected Protocol protocol; - protected URL url; - private HttpURLConnection connection; - protected Map requestHttpHeaders; - protected Map formParams; - protected String boundary; - protected Properties config = null; - protected SSLSocketFactory sslSocketFactory; - protected HostnameVerifier hostnameVerifier; - - protected DataUrlResponse result; - - public String getProtocol() { - if (protocol == null) { - return null; - } - return protocol.toString(); - } - - /** - * opens a connection sets the headers gets the server certificate - * - * @throws java.net.SocketTimeoutException - * @throws java.io.IOException - * @pre url != null - * @pre httpHeaders != null - */ - public void connect() throws SocketTimeoutException, IOException { - connection = (HttpURLConnection) url.openConnection(); - if (connection instanceof HttpsURLConnection) { - HttpsURLConnection https = (HttpsURLConnection) connection; - if (sslSocketFactory != null) { - log.debug("Setting custom ssl socket factory for ssl connection"); - https.setSSLSocketFactory(sslSocketFactory); - } - if (hostnameVerifier != null) { - log.debug("Setting custom hostname verifier"); - https.setHostnameVerifier(hostnameVerifier); - } - } - connection.setDoOutput(true); - Set headers = requestHttpHeaders.keySet(); - Iterator headerIt = headers.iterator(); - while (headerIt.hasNext()) { - String name = headerIt.next(); - connection.setRequestProperty(name, requestHttpHeaders.get(name)); - } - log.trace("Connecting to: "+url); - connection.connect(); - if (connection instanceof HttpsURLConnection) { - HttpsURLConnection ssl = (HttpsURLConnection) connection; - X509Certificate[] certs = (X509Certificate[]) ssl.getServerCertificates(); - if ((certs != null) && (certs.length >= 1)) { - log.trace("Server certificate: "+certs[0]); - serverCertificate = certs[0]; - } - } - } - - public X509Certificate getServerCertificate() { - return serverCertificate; - } - - public void setHTTPHeader(String name, String value) { - if (name != null && value != null) { - requestHttpHeaders.put(name, value); - } - } - - public void setHTTPFormParameter(String name, InputStream data, - String contentType, String charSet, String transferEncoding) { - StringBuilder sb = new StringBuilder(); - try { - InputStreamReader reader = new InputStreamReader(data, (charSet != null) ? charSet : "UTF-8"); - char[] c = new char[512]; - for (int l; (l = reader.read(c)) != -1;) { - sb.append(c, 0, l); - } - } catch (IOException e) { - throw new SLRuntimeException("Failed to set HTTP form parameter.", e); - } - formParams.put(name, sb.toString()); - } - - /** - * send all formParameters - * - * @throws java.io.IOException - */ - public void transmit(SLResult slResult) throws IOException { - StringWriter writer = new StringWriter(); - slResult.writeTo(new StreamResult(writer)); - formParams.put( - (slResult.getResultType() == SLResultType.XML) - ? DataUrlConnection.FORMPARAM_XMLRESPONSE - : DataUrlConnection.FORMPARAM_BINARYRESPONSE, - writer.toString()); - - OutputStream os = connection.getOutputStream(); - OutputStreamWriter streamWriter = new OutputStreamWriter(os, HttpUtil.DEFAULT_CHARSET); - - log.trace("Sending data"); - Iterator keys = formParams.keySet().iterator(); - while(keys.hasNext()) { - String key = keys.next(); - streamWriter.write(URLEncoder.encode(key, "UTF-8")); - streamWriter.write("="); - streamWriter.write(URLEncoder.encode(formParams.get(key), "UTF-8")); - if (keys.hasNext()) { - streamWriter.write("&"); - } - } - streamWriter.flush(); - os.close(); - - // MultipartRequestEntity PostMethod - InputStream is = null; - try { - is = connection.getInputStream(); - } catch (IOException iox) { - log.info(iox); - } - log.trace("Reading response"); - result = new DataUrlResponse(url.toString(), connection.getResponseCode(), is); - Map responseHttpHeaders = new HashMap(); - Map> httpHeaders = connection.getHeaderFields(); - for (Iterator keyIt = httpHeaders.keySet().iterator(); keyIt - .hasNext();) { - String key = keyIt.next(); - StringBuffer value = new StringBuffer(); - for (String val : httpHeaders.get(key)) { - value.append(val); - value.append(HttpUtil.SEPERATOR[0]); - } - String valString = value.substring(0, value.length() - 1); - if ((key != null) && (value.length() > 0)) { - responseHttpHeaders.put(key, valString); - } - } - result.setResponseHttpHeaders(responseHttpHeaders); - } - - @Override - public DataUrlResponse getResponse() throws IOException { - return result; - } - - /** - * inits protocol, url, httpHeaders, formParams - * - * @param url - * must not be null - */ - @Override - public void init(URL url) { - - for (int i = 0; i < SUPPORTED_PROTOCOLS.length; i++) { - if (SUPPORTED_PROTOCOLS[i].toString().equalsIgnoreCase(url.getProtocol())) { - protocol = SUPPORTED_PROTOCOLS[i]; - break; - } - } - if (protocol == null) { - throw new SLRuntimeException("Protocol " + url.getProtocol() - + " not supported for data url"); - } - this.url = url; - requestHttpHeaders = new HashMap(); - if ((config != null) - && (config.getProperty(Configurator.USERAGENT_CONFIG_P) != null)) { - log.debug("setting User-Agent header: " + config.getProperty(Configurator.USERAGENT_CONFIG_P)); - requestHttpHeaders.put(HttpUtil.HTTP_HEADER_USER_AGENT, config - .getProperty(Configurator.USERAGENT_CONFIG_P)); - } else { - requestHttpHeaders - .put(HttpUtil.HTTP_HEADER_USER_AGENT, Configurator.USERAGENT_DEFAULT); - - } - requestHttpHeaders.put(HttpUtil.HTTP_HEADER_CONTENT_TYPE, - HttpUtil.APPLICATION_URL_ENCODED); - - formParams = new HashMap(); - } - - @Override - public DataUrlConnectionSPI newInstance() { - DataUrlConnectionSPI uc = new LegacyDataUrlConnectionImpl(); - uc.setConfiguration(config); - uc.setSSLSocketFactory(sslSocketFactory); - uc.setHostnameVerifier(hostnameVerifier); - return uc; - } - - @Override - public URL getUrl() { - return url; - } - - @Override - public void setConfiguration(Properties config) { - this.config = config; - } - - @Override - public void setSSLSocketFactory(SSLSocketFactory socketFactory) { - this.sslSocketFactory = socketFactory; - } - - @Override - public void setHostnameVerifier(HostnameVerifier hostnameVerifier) { - this.hostnameVerifier = hostnameVerifier; - } -} \ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputDecoder.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputDecoder.java index f4ebe288..69c659e1 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputDecoder.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputDecoder.java @@ -16,86 +16,43 @@ */ package at.gv.egiz.bku.binding; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URLDecoder; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; +import java.io.InputStream; +import java.util.Iterator; +import java.util.Map; + +import org.apache.commons.fileupload.ParameterParser; -import org.apache.commons.fileupload.ParameterParser; - -import at.gv.egiz.bku.slexceptions.SLRuntimeException; -import at.gv.egiz.bku.utils.StreamUtil; - -/** - * Implementation based on Java's URLDecoder class - * - */ -// FIXME replace this code by a streaming variant public class XWWWFormUrlInputDecoder implements InputDecoder { - - public final static String CHAR_SET = "charset"; - public final static String NAME_VAL_SEP = "="; - public final static String SEP = "\\&"; - - private String contentType; - private InputStream dataStream; - private String charset = "UTF-8"; - - protected List decodeInput(InputStream is) throws IOException { - List result = new LinkedList(); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - StreamUtil.copyStream(is, bos); - String inputString = new String(bos.toByteArray()); - String[] nameValuePairs = inputString.split(SEP); - //inputString = URLDecoder.decode(inputString, charset); - for (int i = 0; i < nameValuePairs.length; i++) { - String[] fields = nameValuePairs[i].split(NAME_VAL_SEP, 2); - if (fields.length != 2) { - throw new SLRuntimeException("Invalid form encoding, missing value"); - } - String name = URLDecoder.decode(fields[0], charset); - String value =URLDecoder.decode(fields[1], charset); - ByteArrayInputStream bais = new ByteArrayInputStream(value - .getBytes(charset)); - FormParameterImpl fpi = new FormParameterImpl(contentType, name, bais, null); - result.add(fpi); - } - return result; - } - - @SuppressWarnings("unchecked") + + /** + * The MIME type 'application/x-www-form-urlencoded'. + */ + public static final String CONTENT_TYPE = "application/x-www-form-urlencoded"; + + /** + * The form parameter iterator. + */ + protected XWWWFormUrlInputIterator iterator; + + @SuppressWarnings("unchecked") @Override public void setContentType(String contentType) { ParameterParser pp = new ParameterParser(); pp.setLowerCaseNames(true); Map params = pp.parse(contentType, new char[] { ':', ';' }); - if (!params.containsKey("application/x-www-form-urlencoded")) { + if (!params.containsKey(CONTENT_TYPE)) { throw new IllegalArgumentException( "not a url encoded content type specification: " + contentType); } - String cs = params.get(CHAR_SET); - if (cs != null) { - charset = cs; - } - this.contentType = contentType; } @Override public Iterator getFormParameterIterator() { - try { - return decodeInput(dataStream).iterator(); - } catch (IOException e) { - throw new SLRuntimeException(e); - } + return iterator; } @Override public void setInputStream(InputStream is) { - dataStream = is; + iterator = new XWWWFormUrlInputIterator(is); } } diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputIterator.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputIterator.java new file mode 100644 index 00000000..f052ce05 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/XWWWFormUrlInputIterator.java @@ -0,0 +1,376 @@ +package at.gv.egiz.bku.binding; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +public class XWWWFormUrlInputIterator implements Iterator { + + public static final byte NAME_VALUE_SEP = '='; + + public static final byte PARAM_SEP = '&'; + + public static final Charset UTF_8 = Charset.forName("UTF-8"); + + /** + * The default buffer size. + */ + protected static final int DEFAULT_BUFFER_SIZE = 4096; + + /** + * Are we done with parsing the input. + */ + protected boolean done = false; + + /** + * The x-www-formdata-urlencoded input stream to be parsed. + */ + protected final InputStream in; + + /** + * The buffer size. + */ + protected int bufferSize = DEFAULT_BUFFER_SIZE; + + /** + * The read buffer. + */ + protected final byte[] buf = new byte[bufferSize]; + + /** + * The read position. + */ + protected int pos; + + /** + * The number of valid bytes in the buffer; + */ + protected int count; + + /** + * The parameter returned by the last call of {@link #next()}; + */ + protected XWWWFormUrlEncodedParameter currentParameter; + + /** + * An IOException that cannot be reported immediately. + */ + protected IOException deferredIOException; + + /** + * Creates a new instance of this x-www-formdata-urlencoded input iterator + * with the given InputStream in to be parsed. + * + * @param in the InputStream to be parsed + */ + public XWWWFormUrlInputIterator(InputStream in) { + this.in = in; + } + + /* (non-Javadoc) + * @see java.util.Iterator#hasNext() + */ + @Override + public boolean hasNext() { + if (done) { + return false; + } + if (currentParameter != null) { + // we have to disconnect the current parameter + // to look for further parameters + try { + currentParameter.formParameterValue.disconnect(); + // fill buffer if empty + if (pos >= count) { + if ((count = in.read(buf)) == -1) { + // done + done = true; + return false; + } + pos = 0; + } + } catch (IOException e) { + deferredIOException = e; + } + } + return true; + } + + @Override + public FormParameter next() { + if (hasNext()) { + // skip separator + pos++; + currentParameter = new XWWWFormUrlEncodedParameter(); + return currentParameter; + } else { + throw new NoSuchElementException(); + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + public class XWWWFormUrlEncodedParameter implements FormParameter { + + /** + * The list of header names. + */ + // x-www-form-urlencoded parameters do not provide headers + protected final List headers = Collections.emptyList(); + + /** + * The name of the form parameter. + */ + protected String formParameterName; + + /** + * The value of the form parameter. + */ + protected URLDecodingInputStream formParameterValue; + + public XWWWFormUrlEncodedParameter() { + // parse parameter name + URLDecodingInputStream urldec = new URLDecodingInputStream(in, NAME_VALUE_SEP); + InputStreamReader reader = new InputStreamReader(urldec, UTF_8); + try { + StringBuilder sb = new StringBuilder(); + char[] b = new char[128]; + for (int l = 0; (l = reader.read(b)) != -1;) { + sb.append(b, 0, l); + } + formParameterName = sb.toString(); + // fill buffer if empty + if (pos >= count) { + if ((count = in.read(buf)) == -1) { + throw new IOException("Invalid URL encoding."); + } + pos = 0; + } + // skip separator + pos++; + } catch (IOException e) { + deferredIOException = e; + formParameterName = ""; + } + formParameterValue = new URLDecodingInputStream(in, PARAM_SEP); + } + + @Override + public String getFormParameterContentType() { + // x-www-form-urlencoded parameters do not specify a content type + return null; + } + + @Override + public String getFormParameterName() { + return formParameterName; + } + + @Override + public InputStream getFormParameterValue() { + if (deferredIOException != null) { + final IOException e = deferredIOException; + deferredIOException = null; + return new InputStream() { + @Override + public int read() throws IOException { + throw e; + } + }; + } else { + return formParameterValue; + } + } + + @Override + public Iterator getHeaderNames() { + return headers.iterator(); + } + + @Override + public String getHeaderValue(String headerName) { + return null; + } + + } + + public class URLDecodingInputStream extends FilterInputStream { + + /** + * Has this stream already been closed. + */ + private boolean closed = false; + + /** + * Has this stream been disconnected. + */ + private boolean disconnected = false; + + /** + * Read until this byte occurs. + */ + protected final byte term; + + /** + * Creates a new instance of this URLDecodingInputStream. + * + * @param in + * @param separator + */ + protected URLDecodingInputStream(InputStream in, byte separator) { + super(in); + this.term = separator; + } + + /* (non-Javadoc) + * @see java.io.FilterInputStream#read() + */ + @Override + public int read() throws IOException { + if (closed) { + throw new IOException("The stream has already been closed."); + } + if (disconnected) { + return in.read(); + } + + if (pos >= count) { + if ((count = in.read(buf)) == -1) { + return -1; + } + pos = 0; + } if (buf[pos] == term) { + return -1; + } else if (buf[pos] == '+') { + pos++; + return ' '; + } else if (buf[pos] == '%') { + if (++pos == count) { + if ((count = in.read(buf)) == -1) { + throw new IOException("Invalid URL encoding."); + } + pos = 0; + } + int c1 = Character.digit(buf[pos], 16); + if (++pos == count) { + if ((count = in.read(buf)) == -1) { + throw new IOException("Invalid URL encoding."); + } + pos = 0; + } + int c2 = Character.digit(buf[pos], 16); + return ((c1 << 4) | c2); + } else { + return buf[pos++]; + } + } + + /* (non-Javadoc) + * @see java.io.FilterInputStream#read(byte[], int, int) + */ + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (closed) { + throw new IOException("The stream has already been closed."); + } + if (disconnected) { + return in.read(b, off, len); + } + + if ((off | len | (off + len) | (b.length - (off + len))) < 0) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + + if (pos >= count) { + if ((count = in.read(buf)) == -1) { + return -1; + } + pos = 0; + } + if (buf[pos] == term) { + return -1; + } + + int l = 0; + for (;;) { + while (pos < count) { + if (l == len || buf[pos] == term) { + return l; + } else if (buf[pos] == '+') { + b[off] = ' '; + } else if (buf[pos] == '%') { + if (++pos == count && (count = in.read(buf)) == -1) { + throw new IOException("Invalid URL encoding."); + } + int c1 = Character.digit(buf[pos], 16); + if (++pos == count && (count = in.read(buf)) == -1) { + throw new IOException("Invalid URL encoding."); + } + int c2 = Character.digit(buf[pos], 16); + b[off] = (byte) ((c1 << 4) | c2); + } else { + b[off] = buf[pos]; + } + pos++; + off++; + l++; + } + if ((count = in.read(buf)) == -1) { + return l; + } + pos = 0; + } + } + + /** + * Disconnect from the InputStream and buffer all remaining data. + * + * @throws IOException + */ + public void disconnect() throws IOException { + if (!disconnected) { + // don't waste space for a buffer if end of stream has already been + // reached + byte[] b = new byte[1]; + if ((read(b)) != -1) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + os.write(b); + b = new byte[1024]; + for (int l; (l = read(b, 0, b.length)) != -1;) { + os.write(b, 0, l); + } + super.in = new ByteArrayInputStream(os.toByteArray()); + } + disconnected = true; + } + } + + /* (non-Javadoc) + * @see java.io.FilterInputStream#close() + */ + @Override + public void close() throws IOException { + if (!hasNext()) { + // don't close the underlying stream until all parts are read + super.close(); + } + disconnect(); + closed = true; + } + + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/binding/multipart/SLResultPart.java b/bkucommon/src/main/java/at/gv/egiz/bku/binding/multipart/SLResultPart.java index 5585f02e..d896ea9f 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/binding/multipart/SLResultPart.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/binding/multipart/SLResultPart.java @@ -16,37 +16,56 @@ */ package at.gv.egiz.bku.binding.multipart; +import at.gv.egiz.bku.binding.DataUrlConnection; import at.gv.egiz.bku.slcommands.SLResult; +import at.gv.egiz.bku.slcommands.SLResult.SLResultType; + import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import javax.xml.transform.stream.StreamResult; -import org.apache.commons.httpclient.methods.multipart.ByteArrayPartSource; import org.apache.commons.httpclient.methods.multipart.FilePart; +import org.apache.commons.httpclient.methods.multipart.PartSource; -/** - * - * @author clemens - */ public class SLResultPart extends FilePart { protected SLResult slResult; protected String encoding; public SLResultPart(SLResult slResult, String encoding) { - super("XMLResponse", - new ByteArrayPartSource(null, "dummySource".getBytes())); + super((slResult.getResultType() == SLResultType.XML) + ? DataUrlConnection.FORMPARAM_XMLRESPONSE + : DataUrlConnection.FORMPARAM_BINARYRESPONSE, + new PartSource() { + + @Override + public long getLength() { + // may return null, as sendData() is overridden + return 0; + } + + @Override + public String getFileName() { + // return null, to prevent content-disposition header + return null; + } + + @Override + public InputStream createInputStream() throws IOException { + // may return null, as sendData() is overridden below + return null; + } + } + ); this.slResult = slResult; this.encoding = encoding; } @Override protected void sendData(OutputStream out) throws IOException { - slResult.writeTo(new StreamResult(new OutputStreamWriter(out, encoding))); - // slResult.writeTo(new StreamResult(new OutputStreamWriter(System.out, - // encoding))); - // super.sendData(out); + slResult.writeTo(new StreamResult(new OutputStreamWriter(out, encoding)), false); } } diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/conf/CertValidatorImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/conf/CertValidatorImpl.java index 125233c1..3b2d1b99 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/conf/CertValidatorImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/conf/CertValidatorImpl.java @@ -1,7 +1,9 @@ package at.gv.egiz.bku.conf; +import iaik.logging.LogConfigurationException; import iaik.logging.TransactionId; import iaik.logging.impl.TransactionIdImpl; +import iaik.logging.LoggerConfig; import iaik.pki.DefaultPKIConfiguration; import iaik.pki.DefaultPKIProfile; import iaik.pki.PKIConfiguration; @@ -18,6 +20,7 @@ import iaik.x509.X509Certificate; import java.io.File; import java.util.Date; +import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -37,6 +40,27 @@ public class CertValidatorImpl implements CertValidator { * @see at.gv.egiz.bku.conf.CertValidator#init(java.io.File, java.io.File) */ public void init(File certDir, File caDir) { + // initialize IAIK logging for PKI module + log.debug("Configuring logging for IAIK PKI module"); + iaik.logging.LogFactory.configure(new LoggerConfig() { + + @Override + public Properties getProperties() throws LogConfigurationException { + return null; + } + + @Override + public String getNodeId() { + return "pki"; + } + + @Override + public String getFactory() { + return IAIKCommonsLogFactory.class.getName(); + } + }); + + // the parameters specifying the directory certstore CertStoreParameters[] certStoreParameters = { new DefaultDirectoryCertStoreParameters( "CS-001", certDir.getAbsolutePath(), true, false) }; diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/conf/IAIKCommonsLog.java b/bkucommon/src/main/java/at/gv/egiz/bku/conf/IAIKCommonsLog.java new file mode 100644 index 00000000..1b7dd189 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/conf/IAIKCommonsLog.java @@ -0,0 +1,144 @@ +/** + * + */ +package at.gv.egiz.bku.conf; + +import iaik.logging.Log; +import iaik.logging.TransactionId; + +/** + * @author mcentner + * + */ +public class IAIKCommonsLog implements Log { + + /** + * The id that will be written to the log if the transactionid == null + */ + public final static String NO_ID = "Null-ID"; + + protected org.apache.commons.logging.Log commonsLog; + + protected String nodeId; + + public IAIKCommonsLog(org.apache.commons.logging.Log log) { + this.commonsLog = log; + } + + /* (non-Javadoc) + * @see iaik.logging.Log#debug(iaik.logging.TransactionId, java.lang.Object, java.lang.Throwable) + */ + @Override + public void debug(TransactionId transactionId, Object message, Throwable t) { + if (commonsLog.isDebugEnabled()) { + commonsLog.debug(nodeId + ": " + + ((transactionId != null) ? transactionId.getLogID() : NO_ID) + ": " + + message, t); + } + } + + /* (non-Javadoc) + * @see iaik.logging.Log#info(iaik.logging.TransactionId, java.lang.Object, java.lang.Throwable) + */ + @Override + public void info(TransactionId transactionId, Object message, Throwable t) { + if (commonsLog.isInfoEnabled()) { + commonsLog.info(nodeId + ": " + + ((transactionId != null) ? transactionId.getLogID() : NO_ID) + ": " + + message, t); + } + } + + /* (non-Javadoc) + * @see iaik.logging.Log#warn(iaik.logging.TransactionId, java.lang.Object, java.lang.Throwable) + */ + @Override + public void warn(TransactionId transactionId, Object message, Throwable t) { + if (commonsLog.isWarnEnabled()) { + commonsLog.warn(nodeId + ": " + + ((transactionId != null) ? transactionId.getLogID() : NO_ID) + ": " + + message, t); + } + } + + /* (non-Javadoc) + * @see iaik.logging.Log#error(iaik.logging.TransactionId, java.lang.Object, java.lang.Throwable) + */ + @Override + public void error(TransactionId transactionId, Object message, Throwable t) { + if (commonsLog.isErrorEnabled()) { + commonsLog.error(nodeId + ": " + + ((transactionId != null) ? transactionId.getLogID() : NO_ID) + ": " + + message, t); + } + } + + /* (non-Javadoc) + * @see iaik.logging.Log#fatal(iaik.logging.TransactionId, java.lang.Object, java.lang.Throwable) + */ + @Override + public void fatal(TransactionId transactionId, Object message, Throwable t) { + if (commonsLog.isFatalEnabled()) { + commonsLog.fatal(nodeId + ": " + + ((transactionId != null) ? transactionId.getLogID() : NO_ID) + ": " + + message, t); + } + } + + /* (non-Javadoc) + * @see iaik.logging.Log#setNodeId(java.lang.String) + */ + @Override + public void setNodeId(String nodeId) { + this.nodeId = nodeId; + } + + /* (non-Javadoc) + * @see iaik.logging.Log#getNodeId() + */ + @Override + public String getNodeId() { + return nodeId; + } + + /* (non-Javadoc) + * @see iaik.logging.Log#isDebugEnabled() + */ + @Override + public boolean isDebugEnabled() { + return commonsLog.isDebugEnabled(); + } + + /* (non-Javadoc) + * @see iaik.logging.Log#isInfoEnabled() + */ + @Override + public boolean isInfoEnabled() { + return commonsLog.isInfoEnabled(); + } + + /* (non-Javadoc) + * @see iaik.logging.Log#isWarnEnabled() + */ + @Override + public boolean isWarnEnabled() { + return commonsLog.isWarnEnabled(); + } + + /* (non-Javadoc) + * @see iaik.logging.Log#isErrorEnabled() + */ + @Override + public boolean isErrorEnabled() { + return commonsLog.isErrorEnabled(); + } + + /* (non-Javadoc) + * @see iaik.logging.Log#isFatalEnabled() + */ + @Override + public boolean isFatalEnabled() { + return commonsLog.isFatalEnabled(); + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/conf/IAIKCommonsLogFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/conf/IAIKCommonsLogFactory.java new file mode 100644 index 00000000..14e2c757 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/conf/IAIKCommonsLogFactory.java @@ -0,0 +1,59 @@ +/** + * + */ +package at.gv.egiz.bku.conf; + +import org.apache.commons.logging.impl.WeakHashtable; + +import iaik.logging.Log; +import iaik.logging.LogConfigurationException; +import iaik.logging.LogFactory; + +/** + * @author mcentner + * + */ +public class IAIKCommonsLogFactory extends LogFactory { + + protected WeakHashtable instances = new WeakHashtable(); + + /* (non-Javadoc) + * @see iaik.logging.LogFactory#getInstance(java.lang.String) + */ + @Override + public Log getInstance(String name) throws LogConfigurationException { + org.apache.commons.logging.Log commonsLog = org.apache.commons.logging.LogFactory.getLog(name); + Log log = (Log) instances.get(commonsLog); + if (log == null) { + log = new IAIKCommonsLog(commonsLog); + log.setNodeId(node_id_); + instances.put(commonsLog, log); + } + return log; + } + + /* (non-Javadoc) + * @see iaik.logging.LogFactory#getInstance(java.lang.Class) + */ + @SuppressWarnings("unchecked") + @Override + public Log getInstance(Class clazz) throws LogConfigurationException { + org.apache.commons.logging.Log commonsLog = org.apache.commons.logging.LogFactory.getLog(clazz); + Log log = (Log) instances.get(commonsLog); + if (log == null) { + log = new IAIKCommonsLog(commonsLog); + log.setNodeId(node_id_); + instances.put(commonsLog, log); + } + return log; + } + + /* (non-Javadoc) + * @see iaik.logging.LogFactory#release() + */ + @Override + public void release() { + instances.clear(); + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandFactory.java index fe27bc54..8e3f6ece 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandFactory.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandFactory.java @@ -28,6 +28,7 @@ import javax.xml.bind.JAXBException; import javax.xml.bind.UnmarshalException; import javax.xml.bind.Unmarshaller; import javax.xml.bind.ValidationEvent; +import javax.xml.bind.ValidationEventLocator; import javax.xml.namespace.QName; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLInputFactory; @@ -46,11 +47,11 @@ import at.gv.egiz.bku.slexceptions.SLCommandException; import at.gv.egiz.bku.slexceptions.SLExceptionMessages; import at.gv.egiz.bku.slexceptions.SLRequestException; import at.gv.egiz.bku.slexceptions.SLRuntimeException; +import at.gv.egiz.bku.slexceptions.SLVersionException; import at.gv.egiz.bku.utils.DebugReader; import at.gv.egiz.slbinding.RedirectEventFilter; import at.gv.egiz.slbinding.RedirectUnmarshallerListener; -import at.gv.egiz.validation.ValidationEventLogger; -import javax.xml.bind.ValidationEventHandler; +import at.gv.egiz.validation.ReportingValidationEventHandler; public class SLCommandFactory { @@ -60,7 +61,9 @@ public class SLCommandFactory { public static final String[] SCHEMA_FILES = new String[]{ "at/gv/egiz/bku/slcommands/schema/xml.xsd", "at/gv/egiz/bku/slcommands/schema/xmldsig-core-schema.xsd", - "at/gv/egiz/bku/slcommands/schema/Core-1.2.xsd" + "at/gv/egiz/bku/slcommands/schema/Core-1.2.xsd", + "at/gv/egiz/bku/slcommands/schema/Core.20020225.xsd", + "at/gv/egiz/bku/slcommands/schema/Core.20020831.xsd" }; /** * Logging facility. @@ -169,7 +172,10 @@ public class SLCommandFactory { String slPkg = at.buergerkarte.namespaces.securitylayer._1.ObjectFactory.class.getPackage().getName(); String xmldsigPkg = org.w3._2000._09.xmldsig_.ObjectFactory.class.getPackage().getName(); String cardChannelPkg = at.buergerkarte.namespaces.cardchannel.ObjectFactory.class.getPackage().getName(); - setJaxbContext(JAXBContext.newInstance(slPkg + ":" + xmldsigPkg + ":" + cardChannelPkg)); + String slPkgLegacy1_0 = at.buergerkarte.namespaces.securitylayer._20020225_.ObjectFactory.class.getPackage().getName(); + String slPkgLegacy1_1 = at.buergerkarte.namespaces.securitylayer._20020831_.ObjectFactory.class.getPackage().getName(); + setJaxbContext(JAXBContext.newInstance(slPkg + ":" + xmldsigPkg + ":" + cardChannelPkg + + ":" + slPkgLegacy1_0 + ":" + slPkgLegacy1_1)); } catch (JAXBException e) { log.error("Failed to setup JAXBContext security layer request.", e); throw new SLRuntimeException(e); @@ -248,26 +254,9 @@ public class SLCommandFactory { SLRequestException { Object object; + ReportingValidationEventHandler validationEventHandler = new ReportingValidationEventHandler(); try { -// ValidatorHandler validator = slSchema.newValidatorHandler(); -// validator.getContentHandler(); -// -// SAXParserFactory spf = SAXParserFactory.newInstance(); -// spf.setNamespaceAware(true); -// XMLReader saxReader = spf.newSAXParser().getXMLReader(); -// //TODO extend validator to implement redirectContentHandler (validate+redirect) -// saxReader.setContentHandler(validator); -// //TODO get a InputSource -// SAXSource saxSource = new SAXSource(saxReader, source); -// -// Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); -// //turn off duplicate jaxb validation -// unmarshaller.setSchema(null); -// unmarshaller.setListener(listener); -// unmarshaller.unmarshal(saxSource); - - XMLInputFactory inputFactory = XMLInputFactory.newInstance(); XMLEventReader eventReader = inputFactory.createXMLEventReader(source); RedirectEventFilter redirectEventFilter = new RedirectEventFilter(); @@ -279,7 +268,7 @@ public class SLCommandFactory { unmarshaller.setSchema(slSchema); } log.trace("Before unmarshal()."); - unmarshaller.setEventHandler(new ValidationEventLogger()); + unmarshaller.setEventHandler(validationEventHandler); object = unmarshaller.unmarshal(filteredReader); log.trace("After unmarshal()."); } catch (UnmarshalException e) { @@ -288,6 +277,13 @@ public class SLCommandFactory { } else { log.info("Failed to unmarshall security layer request." + e.getMessage()); } + if (validationEventHandler.getErrorEvent() != null) { + // Validation Error + ValidationEvent errorEvent = validationEventHandler.getErrorEvent(); + ValidationEventLocator locator = errorEvent.getLocator(); + throw new SLRequestException(3002, + SLExceptionMessages.EC3002_INVALID, new Object[]{errorEvent.getMessage()}); + } Throwable cause = e.getCause(); if (cause instanceof SAXParseException) { throw new SLRequestException(3000, @@ -328,10 +324,11 @@ public class SLCommandFactory { * if an unexpected error occurs configuring the unmarshaller, if * unmarshalling fails with an unexpected error or if the * corresponding SLCommand could not be instantiated + * @throws SLVersionException */ @SuppressWarnings("unchecked") public SLCommand createSLCommand(Source source, SLCommandContext context) - throws SLCommandException, SLRuntimeException, SLRequestException { + throws SLCommandException, SLRuntimeException, SLRequestException, SLVersionException { DebugReader dr = null; if (log.isTraceEnabled() && source instanceof StreamSource) { @@ -361,6 +358,12 @@ public class SLCommandFactory { } QName qName = ((JAXBElement) object).getName(); + if (!SLCommand.NAMESPACE_URI.equals(qName.getNamespaceURI())) { + // security layer request version not supported + log.info("Unsupported security layer request version : " + qName.getNamespaceURI()); + throw new SLVersionException(qName.getNamespaceURI()); + } + Class implClass = getImplClass(qName); if (implClass == null) { // command not supported diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLMarshallerFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLMarshallerFactory.java new file mode 100644 index 00000000..e0a375cf --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLMarshallerFactory.java @@ -0,0 +1,172 @@ +/* +* Copyright 2009 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.slcommands; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.bku.slexceptions.SLRuntimeException; +import at.gv.egiz.marshal.MarshallerFactory; + +public class SLMarshallerFactory { + + static Log log = LogFactory.getLog(SLMarshallerFactory.class); + + /** + * The JAXBContext used for result marshaling. + *

+ * Note: Different contexts are used for marshaling and unmarshaling of + * security layer requests and responses to avoid propagation of namespace + * declarations of legacy namespaces into marshaled results. + *

+ * @see #jaxbContextLegacy + */ + protected static JAXBContext context; + + /** + * The JAXBContext used for marshaling of of results in the legacy namespace. + */ + protected static JAXBContext legacyContext; + + // ------------------- initialization on demand idiom ------------------- + // see http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom + // ---------------------------------------------------------------------- + + /** + * Private constructor called by {@link SLMarshallerFactoryInstanceHolder}. + */ + private SLMarshallerFactory() { + // context is initialized immediately while the legacy context is initialized only on demand + try { + String slPkg = at.buergerkarte.namespaces.securitylayer._1.ObjectFactory.class.getPackage().getName(); + String xmldsigPkg = org.w3._2000._09.xmldsig_.ObjectFactory.class.getPackage().getName(); + String cardChannelPkg = at.buergerkarte.namespaces.cardchannel.ObjectFactory.class.getPackage().getName(); + context = JAXBContext.newInstance(slPkg + ":" + xmldsigPkg + ":" + cardChannelPkg); + } catch (JAXBException e) { + log.error("Failed to setup JAXBContext security layer request.", e); + throw new SLRuntimeException(e); + } + } + + /** + * The lazy instance holder for this SLMarshallerFactory. + */ + private static class SLMarshallerFactoryInstanceHolder { + /** + * The instance returned by {@link SLMarshallerFactory#getInstance()} + */ + private static final SLMarshallerFactory instance = new SLMarshallerFactory(); + } + + /** + * Get an instance of the SLMarshallerFactory. + */ + public static SLMarshallerFactory getInstance() { + return SLMarshallerFactoryInstanceHolder.instance; + } + + // ---------------------------------------------------------------------- + + /** + * Initialize the JAXBContext for the legacy namespace. + */ + private static synchronized void ensureLegacyContext() { + // legacy marshaller is initialized only on demand + if (legacyContext == null) { + try { + String slPkgLegacy1_0 = at.buergerkarte.namespaces.securitylayer._20020225_.ObjectFactory.class.getPackage().getName(); + String slPkgLegacy1_1 = at.buergerkarte.namespaces.securitylayer._20020831_.ObjectFactory.class.getPackage().getName(); + String xmldsigPkg = org.w3._2000._09.xmldsig_.ObjectFactory.class.getPackage().getName(); + String cardChannelPkg = at.buergerkarte.namespaces.cardchannel.ObjectFactory.class.getPackage().getName(); + legacyContext = JAXBContext.newInstance(slPkgLegacy1_0 + ":" + slPkgLegacy1_1 + ":" + xmldsigPkg + ":" + cardChannelPkg); + } catch (JAXBException e) { + log.error("Failed to setup JAXBContext security layer request.", e); + throw new SLRuntimeException(e); + } + } + } + + /** + * Creates an SL marshaller. + * + * @param formattedOutput + * true if the marshaller should produce formated + * output, false otherwise + * @return an SL marshaller + */ + public Marshaller createMarshaller(boolean formattedOutput) { + return createMarshaller(formattedOutput, false); + } + + /** + * Creates an SL marshaller. + * + * @param formattedOutput + * true if the marshaller should produce formated + * output, false otherwise + * @param fragment + * true if the marshaller should produce a XML fragment + * (omit XML declaration), false otherwise + * @return an SL marshaller + */ + public Marshaller createMarshaller(boolean formattedOutput, boolean fragment) { + try { + return MarshallerFactory.createMarshaller(context, formattedOutput, fragment); + } catch (JAXBException e) { + log.fatal("Failed to marshall error response.", e); + throw new SLRuntimeException("Failed to marshall error response.", e); + } + } + + /** + * Creates a legacy SL marshaller. + * + * @param formattedOutput + * true if the marshaller should produce formated + * output, false otherwise + * @return a legacy SL marshaller + */ + public Marshaller createLegacyMarshaller(boolean formattedOutput) { + return createLegacyMarshaller(formattedOutput, false); + } + + /** + * Creates a legacy SL marshaller. + * + * @param formattedOutput + * true if the marshaller should produce formated + * output, false otherwise + * @param fragment + * true if the marshaller should produce a XML fragment + * (omit XML declaration), false otherwise + * @return a legacy SL marshaller + */ + public Marshaller createLegacyMarshaller(boolean formattedOutput, boolean fragment) { + try { + ensureLegacyContext(); + return MarshallerFactory.createMarshaller(legacyContext, formattedOutput, fragment); + } catch (JAXBException e) { + log.fatal("Failed to marshall error response.", e); + throw new SLRuntimeException("Failed to marshall error response.", e); + } + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLResult.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLResult.java index 7989a771..e9e483c5 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLResult.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLResult.java @@ -32,12 +32,14 @@ public interface SLResult { */ public String getMimeType(); - public void writeTo(Result aResult); + public void writeTo(Result aResult, boolean fragment); /** * - * @param result + * @param result + * @param fragment TODO * @param transformer may be null. */ - public void writeTo(Result result, Templates templates); + public void writeTo(Result result, Templates templates, boolean fragment); + } \ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AbstractAssocArrayInfobox.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AbstractAssocArrayInfobox.java index ce03dcf9..9a4536e6 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AbstractAssocArrayInfobox.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AbstractAssocArrayInfobox.java @@ -16,7 +16,6 @@ */ package at.gv.egiz.bku.slcommands.impl; -import at.gv.egiz.marshal.NamespacePrefixMapperImpl; import java.io.ByteArrayOutputStream; import java.util.Arrays; import java.util.Collections; @@ -24,7 +23,6 @@ import java.util.List; import java.util.Map; import java.util.regex.Pattern; -import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; @@ -42,10 +40,8 @@ import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadParamsAssocArrayTy import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadParamsAssocArrayType.ReadValue; import at.gv.egiz.bku.slcommands.InfoboxReadResult; import at.gv.egiz.bku.slcommands.SLCommandContext; -import at.gv.egiz.bku.slcommands.SLCommandFactory; +import at.gv.egiz.bku.slcommands.SLMarshallerFactory; import at.gv.egiz.bku.slexceptions.SLCommandException; -import at.gv.egiz.marshal.MarshallerFactory; -import javax.xml.bind.PropertyException; /** * An abstract base class for {@link Infobox} implementations of type associative array. @@ -255,13 +251,10 @@ public abstract class AbstractAssocArrayInfobox extends AbstractInfoboxImpl } protected byte[] marshallValue(Object jaxbElement) throws SLCommandException { - SLCommandFactory commandFactory = SLCommandFactory.getInstance(); - JAXBContext jaxbContext = commandFactory.getJaxbContext(); - ByteArrayOutputStream result; + Marshaller marshaller = SLMarshallerFactory.getInstance().createMarshaller(false); + ByteArrayOutputStream result = new ByteArrayOutputStream(); try { - Marshaller marshaller = MarshallerFactory.createMarshaller(jaxbContext); - result = new ByteArrayOutputStream(); marshaller.marshal(jaxbElement, result); } catch (JAXBException e) { log.info("Failed to marshall infobox content.", e); diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureResultImpl.java index b352a51e..19df4334 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureResultImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureResultImpl.java @@ -16,8 +16,6 @@ */ package at.gv.egiz.bku.slcommands.impl; -import at.gv.egiz.marshal.NamespacePrefixMapperImpl; -import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; @@ -33,10 +31,8 @@ import org.w3c.dom.Node; import at.buergerkarte.namespaces.securitylayer._1.CreateXMLSignatureResponseType; import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory; -import at.gv.egiz.bku.slcommands.SLCommandFactory; +import at.gv.egiz.bku.slcommands.SLMarshallerFactory; import at.gv.egiz.bku.slexceptions.SLRuntimeException; -import at.gv.egiz.marshal.MarshallerFactory; -import javax.xml.bind.PropertyException; /** * This calls implements the result of the security layer command CreateXMLSignature. @@ -86,10 +82,9 @@ public class CreateXMLSignatureResultImpl extends SLResultImpl { JAXBElement createCreateXMLSignatureResponse = factory.createCreateXMLSignatureResponse(createCreateXMLSignatureResponseType); DocumentFragment fragment = doc.createDocumentFragment(); - - JAXBContext jaxbContext = SLCommandFactory.getInstance().getJaxbContext(); + + Marshaller marshaller = SLMarshallerFactory.getInstance().createMarshaller(false); try { - Marshaller marshaller = MarshallerFactory.createMarshaller(jaxbContext); marshaller.marshal(createCreateXMLSignatureResponse, fragment); } catch (JAXBException e) { log.error("Failed to marshall 'CreateXMLSignatureResponse'", e); @@ -105,8 +100,8 @@ public class CreateXMLSignatureResultImpl extends SLResultImpl { } @Override - public void writeTo(Result result, Templates templates) { - writeTo(doc, result, templates); + public void writeTo(Result result, Templates templates, boolean fragment) { + writeTo(doc, result, templates, fragment); } } diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImpl.java index 5d0f0de0..aedde238 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImpl.java @@ -56,11 +56,11 @@ public class ErrorResultImpl extends SLResultImpl implements ErrorResult { } @Override - public void writeTo(Result result, Templates templates) { + public void writeTo(Result result, Templates templates, boolean fragment) { if (locale == null) { - writeErrorTo(slException, result, templates); + writeErrorTo(slException, result, templates, fragment); } else { - writeErrorTo(slException, result, templates, locale); + writeErrorTo(slException, result, templates, locale, fragment); } } diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/GetStatusCommandImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/GetStatusCommandImpl.java index 46bfe18b..0c2b96f9 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/GetStatusCommandImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/GetStatusCommandImpl.java @@ -19,10 +19,8 @@ package at.gv.egiz.bku.slcommands.impl; import at.buergerkarte.namespaces.securitylayer._1.GetStatusRequestType; import at.gv.egiz.bku.slcommands.GetStatusCommand; -import at.gv.egiz.bku.slcommands.SLCommandContext; import at.gv.egiz.bku.slcommands.SLResult; import at.gv.egiz.bku.slexceptions.SLCommandException; -import at.gv.egiz.bku.slexceptions.SLException; import at.gv.egiz.stal.ErrorResponse; import at.gv.egiz.stal.STAL; import at.gv.egiz.stal.STALResponse; diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/GetStatusResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/GetStatusResultImpl.java index fddd3b0b..fb1f627f 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/GetStatusResultImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/GetStatusResultImpl.java @@ -45,8 +45,8 @@ public class GetStatusResultImpl extends SLResultImpl implements GetStatusResult } @Override - public void writeTo(Result result, Templates templates) { + public void writeTo(Result result, Templates templates, boolean fragment) { JAXBElement response = of.createGetStatusResponse(responseType); - writeTo(response, result, templates); + writeTo(response, result, templates, fragment); } } diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/IdentityLinkInfoboxImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/IdentityLinkInfoboxImpl.java index 7a82e43f..160e9589 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/IdentityLinkInfoboxImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/IdentityLinkInfoboxImpl.java @@ -18,7 +18,6 @@ package at.gv.egiz.bku.slcommands.impl; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.net.MalformedURLException; import java.security.cert.X509Certificate; import java.util.ArrayList; diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadResultFileImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadResultFileImpl.java index 75e44afa..422b424f 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadResultFileImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadResultFileImpl.java @@ -16,8 +16,6 @@ */ package at.gv.egiz.bku.slcommands.impl; -import at.gv.egiz.marshal.NamespacePrefixMapperImpl; -import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; @@ -39,10 +37,8 @@ import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory; import at.buergerkarte.namespaces.securitylayer._1.XMLContentType; import at.gv.egiz.bku.slcommands.InfoboxReadResult; import at.gv.egiz.bku.slcommands.SLCommand; -import at.gv.egiz.bku.slcommands.SLCommandFactory; +import at.gv.egiz.bku.slcommands.SLMarshallerFactory; import at.gv.egiz.bku.slexceptions.SLRuntimeException; -import at.gv.egiz.marshal.MarshallerFactory; -import javax.xml.bind.PropertyException; /** * This class implements the result of the security layer command InfoboxReadRequest. @@ -98,10 +94,9 @@ public class InfoboxReadResultFileImpl extends SLResultImpl implements infoboxReadResponseType.setBinaryFileData(base64XMLContentType); JAXBElement infoboxReadResponse = factory.createInfoboxReadResponse(infoboxReadResponseType); - - JAXBContext context = SLCommandFactory.getInstance().getJaxbContext(); + + Marshaller marshaller = SLMarshallerFactory.getInstance().createMarshaller(false); try { - Marshaller marshaller = MarshallerFactory.createMarshaller(context); marshaller.marshal(infoboxReadResponse, doc); } catch (JAXBException e) { log.error("Failed to marshal 'InfoboxReadResponse' document.", e); @@ -158,8 +153,8 @@ public class InfoboxReadResultFileImpl extends SLResultImpl implements } @Override - public void writeTo(Result result, Templates templates) { - writeTo(xmlDocument, result, templates); + public void writeTo(Result result, Templates templates, boolean fragment) { + writeTo(xmlDocument, result, templates, fragment); } } diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadResultImpl.java index e508941d..271ec955 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadResultImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadResultImpl.java @@ -55,10 +55,10 @@ public class InfoboxReadResultImpl extends SLResultImpl implements InfoboxReadRe } @Override - public void writeTo(Result result, Templates templates) { + public void writeTo(Result result, Templates templates, boolean fragment) { ObjectFactory objectFactory = new ObjectFactory(); JAXBElement response = objectFactory.createInfoboxReadResponse(infoboxReadResponse); - writeTo(response, result, templates); + writeTo(response, result, templates, fragment); } } diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxUpdateResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxUpdateResultImpl.java index 15064756..e12536ba 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxUpdateResultImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxUpdateResultImpl.java @@ -36,8 +36,8 @@ public class InfoboxUpdateResultImpl extends SLResultImpl implements } @Override - public void writeTo(Result result, Templates templates) { - writeTo(RESPONSE, result, templates); + public void writeTo(Result result, Templates templates, boolean fragment) { + writeTo(RESPONSE, result, templates, fragment); } } diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/NullOperationResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/NullOperationResultImpl.java index 05986f85..87733e39 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/NullOperationResultImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/NullOperationResultImpl.java @@ -41,8 +41,8 @@ public class NullOperationResultImpl extends SLResultImpl implements NullOperati } @Override - public void writeTo(Result result, Templates templates) { - writeTo(RESPONSE, result, templates); + public void writeTo(Result result, Templates templates, boolean fragment) { + super.writeTo(RESPONSE, result, templates, fragment); } } diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLResultImpl.java index 0452bddf..0077b7b2 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLResultImpl.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLResultImpl.java @@ -17,12 +17,14 @@ package at.gv.egiz.bku.slcommands.impl; import java.io.UnsupportedEncodingException; +import java.math.BigInteger; import java.util.Locale; -import javax.xml.bind.JAXBContext; +import javax.xml.XMLConstants; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; +import javax.xml.transform.OutputKeys; import javax.xml.transform.Result; import javax.xml.transform.Templates; import javax.xml.transform.Transformer; @@ -41,17 +43,15 @@ import org.w3c.dom.Node; import at.buergerkarte.namespaces.securitylayer._1.ErrorResponseType; import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory; -import at.gv.egiz.marshal.NamespacePrefixMapperImpl; -import at.gv.egiz.bku.slcommands.SLCommandFactory; +import at.gv.egiz.bku.slcommands.SLMarshallerFactory; import at.gv.egiz.bku.slcommands.SLResult; import at.gv.egiz.bku.slexceptions.SLBindingException; import at.gv.egiz.bku.slexceptions.SLCommandException; import at.gv.egiz.bku.slexceptions.SLException; import at.gv.egiz.bku.slexceptions.SLRuntimeException; +import at.gv.egiz.bku.slexceptions.SLVersionException; import at.gv.egiz.bku.utils.DebugOutputStream; import at.gv.egiz.bku.utils.DebugWriter; -import at.gv.egiz.marshal.MarshallerFactory; -import javax.xml.bind.PropertyException; /** * This class serves as an abstract base class for the implementation of a @@ -90,20 +90,18 @@ public abstract class SLResultImpl implements SLResult { return resultingMimeType; } - private Marshaller getMarshaller() { - try { - JAXBContext context = SLCommandFactory.getInstance().getJaxbContext(); - Marshaller marshaller = MarshallerFactory.createMarshaller(context, true); - return marshaller; - } catch (JAXBException e) { - log.fatal("Failed to marshall error response.", e); - throw new SLRuntimeException("Failed to marshall error response.", e); - } + @Override + public void writeTo(Result result, boolean fragment) { + writeTo(result, null, false); } + @Override + public abstract void writeTo(Result result, Templates templates, boolean fragment); + private TransformerHandler getTransformerHandler(Templates templates, Result result) throws SLException { try { SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance(); + transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); TransformerHandler transformerHandler = transformerFactory.newTransformerHandler(templates); transformerHandler.setResult(result); return transformerHandler; @@ -119,12 +117,6 @@ public abstract class SLResultImpl implements SLResult { } } - @Override - public void writeTo(Result result) { - writeTo(result, null); - } - - /** * Writes the given response to the SAX result using * the given transform templates. @@ -133,7 +125,7 @@ public abstract class SLResultImpl implements SLResult { * @param result * @param templates */ - protected void writeTo(JAXBElement response, Result result, Templates templates) { + protected void writeTo(JAXBElement response, Result result, Templates templates, boolean fragment) { DebugWriter dw = null; DebugOutputStream ds = null; @@ -154,11 +146,11 @@ public abstract class SLResultImpl implements SLResult { try { transformerHandler = getTransformerHandler(templates, result); } catch (SLException e) { - writeErrorTo(e, result, templates); + writeErrorTo(e, result, templates, fragment); } } - Marshaller marshaller = getMarshaller(); + Marshaller marshaller = SLMarshallerFactory.getInstance().createMarshaller(true); try { if (transformerHandler != null) { marshaller.marshal(response, transformerHandler); @@ -168,7 +160,7 @@ public abstract class SLResultImpl implements SLResult { } catch (JAXBException e) { log.info("Failed to marshall " + response.getName() + " result." , e); SLCommandException commandException = new SLCommandException(4000); - writeErrorTo(commandException, result, templates); + writeErrorTo(commandException, result, templates, fragment); } if (ds != null) { @@ -185,7 +177,7 @@ public abstract class SLResultImpl implements SLResult { } - protected void writeTo(Node node, Result result, Templates templates) { + protected void writeTo(Node node, Result result, Templates templates, boolean fragment) { DebugWriter dw = null; DebugOutputStream ds = null; @@ -205,24 +197,30 @@ public abstract class SLResultImpl implements SLResult { try { TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); + if (fragment) { + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + } transformer.transform(new DOMSource(node), result); } catch (TransformerConfigurationException e) { log.error("Failed to create Transformer.", e); - writeErrorTo(new SLException(4000), result, null); + writeErrorTo(new SLException(4000), result, null, fragment); } catch (TransformerException e) { log.error("Failed to transform result.", e); - writeErrorTo(new SLException(4000), result, null); + writeErrorTo(new SLException(4000), result, null, fragment); } } else { try { Transformer transformer = templates.newTransformer(); + if (fragment) { + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + } transformer.transform(new DOMSource(node), result); } catch (TransformerConfigurationException e) { log.info("Failed to create transformer.", e); - writeErrorTo(new SLException(2008), result, templates); + writeErrorTo(new SLException(2008), result, templates, fragment); } catch (TransformerException e) { log.error("Failed to transform result.", e); - writeErrorTo(new SLException(2008), result, templates); + writeErrorTo(new SLException(2008), result, templates, fragment); } } @@ -240,11 +238,11 @@ public abstract class SLResultImpl implements SLResult { } - protected void writeErrorTo(SLException slException, Result result, Templates templates) { - writeErrorTo(slException, result, templates, Locale.getDefault()); + protected void writeErrorTo(SLException slException, Result result, Templates templates, boolean fragment) { + writeErrorTo(slException, result, templates, Locale.getDefault(), fragment); } - protected void writeErrorTo(SLException slException, Result result, Templates templates, Locale locale) { + protected void writeErrorTo(SLException slException, Result result, Templates templates, Locale locale, boolean fragment) { TransformerHandler transformerHandler = null; if (templates != null) { @@ -256,13 +254,33 @@ public abstract class SLResultImpl implements SLResult { } } - ObjectFactory factory = new ObjectFactory(); - ErrorResponseType responseType = factory.createErrorResponseType(); - responseType.setErrorCode(slException.getErrorCode()); - responseType.setInfo(slException.getLocalizedMessage(locale)); - JAXBElement response = factory.createErrorResponse(responseType); + Object response; + + Marshaller marshaller; + if (slException instanceof SLVersionException + && ("http://www.buergerkarte.at/namespaces/securitylayer/20020225#" + .equals(((SLVersionException) slException).getNamespaceURI()) || + "http://www.buergerkarte.at/namespaces/securitylayer/20020831#" + .equals(((SLVersionException) slException).getNamespaceURI()))) { + // issue ErrorResponse in the legacy namespace + at.buergerkarte.namespaces.securitylayer._20020225_.ObjectFactory factory + = new at.buergerkarte.namespaces.securitylayer._20020225_.ObjectFactory(); + at.buergerkarte.namespaces.securitylayer._20020225_.ErrorResponseType errorResponseType = factory + .createErrorResponseType(); + errorResponseType.setErrorCode(BigInteger.valueOf(slException + .getErrorCode())); + errorResponseType.setInfo(slException.getLocalizedMessage(locale)); + response = factory.createErrorResponse(errorResponseType); + marshaller = SLMarshallerFactory.getInstance().createLegacyMarshaller(true, fragment); + } else { + ObjectFactory factory = new ObjectFactory(); + ErrorResponseType responseType = factory.createErrorResponseType(); + responseType.setErrorCode(slException.getErrorCode()); + responseType.setInfo(slException.getLocalizedMessage(locale)); + response = factory.createErrorResponse(responseType); + marshaller = SLMarshallerFactory.getInstance().createMarshaller(true, fragment); + } - Marshaller marshaller = getMarshaller(); try { if (transformerHandler != null) { marshaller.marshal(response, transformerHandler); diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/DataObject.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/DataObject.java index b64306aa..2088a684 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/DataObject.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/DataObject.java @@ -81,7 +81,6 @@ import at.gv.egiz.bku.viewer.ValidationException; import at.gv.egiz.bku.viewer.Validator; import at.gv.egiz.bku.viewer.ValidatorFactory; import at.gv.egiz.dom.DOMUtils; -import at.gv.egiz.marshal.NamespacePrefix; import at.gv.egiz.marshal.NamespacePrefixMapperImpl; import at.gv.egiz.slbinding.impl.XMLContentType; import javax.xml.namespace.NamespaceContext; diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/Signature.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/Signature.java index 9182e824..26ddb153 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/Signature.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/Signature.java @@ -16,7 +16,6 @@ */ package at.gv.egiz.bku.slcommands.impl.xsect; -import at.gv.egiz.stal.HashDataInput; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -31,9 +30,7 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; import java.util.Date; -import java.util.HashMap; import java.util.List; -import java.util.Map; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; @@ -87,8 +84,6 @@ import at.gv.egiz.bku.utils.urldereferencer.StreamData; import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer; import at.gv.egiz.bku.utils.urldereferencer.URLDereferencerContext; import at.gv.egiz.dom.DOMUtils; -import at.gv.egiz.marshal.NamespacePrefix; -import at.gv.egiz.marshal.NamespacePrefixMapperImpl; import at.gv.egiz.slbinding.impl.XMLContentType; import at.gv.egiz.stal.STAL; import at.gv.egiz.xades.QualifyingPropertiesException; @@ -327,6 +322,8 @@ public class Signature { */ public void buildXMLSignature() throws SLCommandException { + String signatureId = ctx.getIdValueFactory().createIdValue("Signature"); + List objects = new ArrayList(); List references = new ArrayList(); @@ -340,7 +337,7 @@ public class Signature { } } - addXAdESObjectAndReference(objects, references); + addXAdESObjectAndReference(objects, references, signatureId); XMLSignatureFactory signatureFactory = ctx.getSignatureFactory(); AlgorithmMethodFactory algorithmMethodFactory = ctx.getAlgorithmMethodFactory(); @@ -369,7 +366,6 @@ public class Signature { ki = kif.newKeyInfo(Collections.singletonList(x509Data)); } - String signatureId = ctx.getIdValueFactory().createIdValue("Signature"); String signatureValueId = ctx.getIdValueFactory().createIdValue("SignatureValue"); xmlSignature = signatureFactory.newXMLSignature(si, ki, objects, signatureId, signatureValueId); @@ -588,7 +584,7 @@ public class Signature { * @param references * the list of ds:References to add the created * ds:Reference to - * + * @param signatureId TODO * @throws SLCommandException * if creating and adding the XAdES * QualifyingProperties fails @@ -596,7 +592,7 @@ public class Signature { * if objects or references is * null */ - private void addXAdESObjectAndReference(List objects, List references) throws SLCommandException { + private void addXAdESObjectAndReference(List objects, List references, String signatureId) throws SLCommandException { QualifyingPropertiesFactory factory = QualifyingPropertiesFactory.getInstance(); @@ -630,9 +626,11 @@ public class Signature { } } + String target = "#" + signatureId; + JAXBElement qualifyingProperties; try { - qualifyingProperties = factory.createQualifyingProperties111(date, signingCertificates, idValue, dataObjectFormats); + qualifyingProperties = factory.createQualifyingProperties111(target, date, signingCertificates, idValue, dataObjectFormats); } catch (QualifyingPropertiesException e) { log.error("Failed to create QualifyingProperties.", e); throw new SLCommandException(4000); diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLExceptionMessages.java b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLExceptionMessages.java index 5ce5cba1..73ac8d1b 100644 --- a/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLExceptionMessages.java +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLExceptionMessages.java @@ -47,4 +47,10 @@ public final class SLExceptionMessages { public static final String EC4011_NOTIMPLEMENTED = "ec4011.notimplemented"; + // + // Legacy error codes + // + + public static final String LEC2901_NOTIMPLEMENTED = "lec2901.notimplemented"; + } diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLVersionException.java b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLVersionException.java new file mode 100644 index 00000000..45501746 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slexceptions/SLVersionException.java @@ -0,0 +1,28 @@ +package at.gv.egiz.bku.slexceptions; + +public class SLVersionException extends SLException { + + private static final long serialVersionUID = 1L; + + protected String namespaceURI; + + public SLVersionException(String namespaceURI) { + super(2901, SLExceptionMessages.LEC2901_NOTIMPLEMENTED, new Object[] {namespaceURI}); + this.namespaceURI = namespaceURI; + } + + public SLVersionException(int errorCode, String namespaceURI) { + super(errorCode); + this.namespaceURI = namespaceURI; + } + + public SLVersionException(int errorCode, String namespaceURI, String message, Object[] arguments) { + super(errorCode, message, arguments); + this.namespaceURI = namespaceURI; + } + + public String getNamespaceURI() { + return namespaceURI; + } + +} diff --git a/bkucommon/src/main/resources/at/gv/egiz/bku/slcommands/schema/Core.20020225.xsd b/bkucommon/src/main/resources/at/gv/egiz/bku/slcommands/schema/Core.20020225.xsd new file mode 100644 index 00000000..76d1d7cb --- /dev/null +++ b/bkucommon/src/main/resources/at/gv/egiz/bku/slcommands/schema/Core.20020225.xsd @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bkucommon/src/main/resources/at/gv/egiz/bku/slcommands/schema/Core.20020831.xsd b/bkucommon/src/main/resources/at/gv/egiz/bku/slcommands/schema/Core.20020831.xsd new file mode 100644 index 00000000..6759d791 --- /dev/null +++ b/bkucommon/src/main/resources/at/gv/egiz/bku/slcommands/schema/Core.20020831.xsd @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/bkucommon/src/main/resources/at/gv/egiz/bku/slexceptions/SLExceptionMessages.properties b/bkucommon/src/main/resources/at/gv/egiz/bku/slexceptions/SLExceptionMessages.properties index 73409c8b..db56184e 100644 --- a/bkucommon/src/main/resources/at/gv/egiz/bku/slexceptions/SLExceptionMessages.properties +++ b/bkucommon/src/main/resources/at/gv/egiz/bku/slexceptions/SLExceptionMessages.properties @@ -95,5 +95,10 @@ ec4000.infobox.invalid=Die Infobox '{0}' enth ec4000.idlink.transfomation.failed=Die komprimierte Personenbindung konnte mit dem Stylesheet {0} nicht transformiert werden. ec4002.infobox.unknown=Unbekannter Infoboxbezeichner {0}. ec4003.not.resolved=Zu signierendes Datum kann nicht aufgelöst werden (URI={0}). -ec4011.notimplemented=Befehl {0} ist nicht implementiert. +ec4011.notimplemented=Befehl {0} ist nicht implementiert. + +# Legacy error messages +# + +lec2901.notimplemented=Die in der Anfrage verwendete Version des Security-Layer Protokolls ({0}) wird nicht mehr unterstützt. diff --git a/bkucommon/src/main/resources/at/gv/egiz/bku/slexceptions/SLExceptionMessages_en.properties b/bkucommon/src/main/resources/at/gv/egiz/bku/slexceptions/SLExceptionMessages_en.properties index 91ca20e8..6c67ba87 100644 --- a/bkucommon/src/main/resources/at/gv/egiz/bku/slexceptions/SLExceptionMessages_en.properties +++ b/bkucommon/src/main/resources/at/gv/egiz/bku/slexceptions/SLExceptionMessages_en.properties @@ -96,3 +96,7 @@ ec4000.idlink.transfomation.failed=Failed to transform CompressedIdentityLink wi ec4002.infobox.unknown=Unknown info box identifier {0}. ec4003.not.resolved=Data to be signed cannot be resolved from URI={0}. ec4011.notimplemented=Command {0} not implemented. + +# Legacy error codes +# +lec2901.notimplemented=The version ({0}) of the security-layer protocol used in the request is not supported. diff --git a/bkucommon/src/test/java/at/gv/egiz/bku/binding/XWWWFormUrlInputIteratorTest.java b/bkucommon/src/test/java/at/gv/egiz/bku/binding/XWWWFormUrlInputIteratorTest.java new file mode 100644 index 00000000..703e4460 --- /dev/null +++ b/bkucommon/src/test/java/at/gv/egiz/bku/binding/XWWWFormUrlInputIteratorTest.java @@ -0,0 +1,152 @@ +package at.gv.egiz.bku.binding; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.URLEncoder; +import java.nio.charset.Charset; + +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +public class XWWWFormUrlInputIteratorTest { + + @Test + public void testOneParam() throws IOException { + + final String name = "name"; + final String value = "value"; + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + OutputStreamWriter w = new OutputStreamWriter(os, Charset.forName("UTF-8")); + w.write(name); + w.write("="); + w.write(value); + w.flush(); + w.close(); + + ByteArrayInputStream in = new ByteArrayInputStream(os.toByteArray()); + XWWWFormUrlInputIterator decoder = new XWWWFormUrlInputIterator(in); + + assertTrue(decoder.hasNext()); + FormParameter param = decoder.next(); + assertNotNull(param); + assertEquals(name, param.getFormParameterName()); + InputStream vis = param.getFormParameterValue(); + assertNotNull(vis); + InputStreamReader r = new InputStreamReader(vis); + char[] buf = new char[value.length() + 1]; + int len = r.read(buf); + assertEquals(value.length(), len); + assertEquals(value, new String(buf, 0, len)); + assertFalse(decoder.hasNext()); + Exception ex = null; + try { + decoder.next(); + } catch (Exception e) { + ex = e; + } + assertNotNull(ex); + + } + + @Test + public void testTwoParam() throws IOException { + + final String name1 = "name"; + final String value1 = "value"; + final String name2 = "Name_2"; + final String value2 = "Value 2"; + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + OutputStreamWriter w = new OutputStreamWriter(os, Charset.forName("UTF-8")); + w.write(name1); + w.write("="); + w.write(value1); + w.write("&"); + w.write(URLEncoder.encode(name2, "UTF-8")); + w.write("="); + w.write(URLEncoder.encode(value2, "UTF-8")); + w.flush(); + w.close(); + + ByteArrayInputStream in = new ByteArrayInputStream(os.toByteArray()); + XWWWFormUrlInputIterator decoder = new XWWWFormUrlInputIterator(in); + + assertTrue(decoder.hasNext()); + FormParameter param = decoder.next(); + assertNotNull(param); + assertEquals(name1, param.getFormParameterName()); + InputStream vis = param.getFormParameterValue(); + assertNotNull(vis); + InputStreamReader r = new InputStreamReader(vis); + char[] buf = new char[value1.length() + 1]; + int len = r.read(buf); + assertEquals(value1.length(), len); + assertEquals(value1, new String(buf, 0, len)); + + assertTrue(decoder.hasNext()); + param = decoder.next(); + assertNotNull(param); + assertEquals(name2, param.getFormParameterName()); + vis = param.getFormParameterValue(); + assertNotNull(vis); + r = new InputStreamReader(vis); + buf = new char[value2.length() + 1]; + len = r.read(buf); + assertEquals(value2.length(), len); + assertEquals(value2, new String(buf, 0, len)); + + assertFalse(decoder.hasNext()); + } + + @Test + public void testURLEnc() throws IOException { + + String name = "name"; + byte[] value = new byte[128]; + for (int i = 0; i < value.length; i++) { + value[i] = (byte) i; + } + + String encValue = URLEncoder.encode(new String(value, "UTF-8"), "ASCII"); + System.out.println(encValue); + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + OutputStreamWriter w = new OutputStreamWriter(os, Charset.forName("UTF-8")); + w.write(name); + w.write("="); + w.write(encValue); + w.flush(); + w.close(); + + ByteArrayInputStream in = new ByteArrayInputStream(os.toByteArray()); + XWWWFormUrlInputIterator decoder = new XWWWFormUrlInputIterator(in); + + assertTrue(decoder.hasNext()); + FormParameter param = decoder.next(); + assertNotNull(param); + assertEquals(name, param.getFormParameterName()); + InputStream vis = param.getFormParameterValue(); + assertNotNull(vis); + byte[] buf = new byte[value.length]; + int len = vis.read(buf); + assertArrayEquals(value, buf); + assertEquals(value.length, len); + assertFalse(decoder.hasNext()); + Exception ex = null; + try { + decoder.next(); + } catch (Exception e) { + ex = e; + } + assertNotNull(ex); + + } + + +} diff --git a/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/SLCommandFactoryTest.java b/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/SLCommandFactoryTest.java index cd931878..7a087b38 100644 --- a/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/SLCommandFactoryTest.java +++ b/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/SLCommandFactoryTest.java @@ -33,6 +33,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; import at.gv.egiz.bku.slexceptions.SLCommandException; import at.gv.egiz.bku.slexceptions.SLRequestException; import at.gv.egiz.bku.slexceptions.SLRuntimeException; +import at.gv.egiz.bku.slexceptions.SLVersionException; import at.gv.egiz.stal.dummy.DummySTAL; public class SLCommandFactoryTest { @@ -54,7 +55,7 @@ public class SLCommandFactoryTest { } @Test - public void createNullOperationCommand() throws SLCommandException, SLRuntimeException, SLRequestException { + public void createNullOperationCommand() throws SLCommandException, SLRuntimeException, SLRequestException, SLVersionException { Reader requestReader = new StringReader( ""); Source source = new StreamSource(requestReader); @@ -65,7 +66,7 @@ public class SLCommandFactoryTest { } @Test(expected=SLCommandException.class) - public void createUnsupportedCommand() throws SLCommandException, SLRuntimeException, SLRequestException { + public void createUnsupportedCommand() throws SLCommandException, SLRuntimeException, SLRequestException, SLVersionException { Reader requestReader = new StringReader( ""); Source source = new StreamSource(requestReader); @@ -75,7 +76,7 @@ public class SLCommandFactoryTest { } @Test(expected=SLRequestException.class) - public void createMalformedCommand() throws SLCommandException, SLRuntimeException, SLRequestException { + public void createMalformedCommand() throws SLCommandException, SLRuntimeException, SLRequestException, SLVersionException { Reader requestReader = new StringReader( "" + "missplacedContent" + diff --git a/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureComandImplTest.java b/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureComandImplTest.java index 8fdec375..4e9b4cd7 100644 --- a/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureComandImplTest.java +++ b/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureComandImplTest.java @@ -41,6 +41,7 @@ import at.gv.egiz.bku.slcommands.impl.xsect.STALProvider; import at.gv.egiz.bku.slexceptions.SLCommandException; import at.gv.egiz.bku.slexceptions.SLRequestException; import at.gv.egiz.bku.slexceptions.SLRuntimeException; +import at.gv.egiz.bku.slexceptions.SLVersionException; import at.gv.egiz.stal.STAL; import at.gv.egiz.stal.dummy.DummySTAL; //@Ignore @@ -66,7 +67,7 @@ public class CreateXMLSignatureComandImplTest { } @Test - public void testCreateXMLSignatureRequest() throws SLCommandException, SLRuntimeException, SLRequestException { + public void testCreateXMLSignatureRequest() throws SLCommandException, SLRuntimeException, SLRequestException, SLVersionException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("at/gv/egiz/bku/slcommands/createxmlsignaturerequest/CreateXMLSignatureRequest.xml"); assertNotNull(inputStream); @@ -76,11 +77,11 @@ public class CreateXMLSignatureComandImplTest { assertTrue(command instanceof CreateXMLSignatureCommand); SLResult result = command.execute(); - result.writeTo(new StreamResult(System.out)); + result.writeTo(new StreamResult(System.out), false); } // @Test(expected=SLCommandException.class) - public void testInfboxReadRequestInvalid1() throws SLCommandException, SLRuntimeException, SLRequestException { + public void testInfboxReadRequestInvalid1() throws SLCommandException, SLRuntimeException, SLRequestException, SLVersionException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("at/gv/egiz/bku/slcommands/infoboxreadcommand/IdentityLink.Binary.Invalid-1.xml"); assertNotNull(inputStream); @@ -90,7 +91,7 @@ public class CreateXMLSignatureComandImplTest { } // @Test(expected=SLCommandException.class) - public void testInfboxReadRequestInvalid2() throws SLCommandException, SLRuntimeException, SLRequestException { + public void testInfboxReadRequestInvalid2() throws SLCommandException, SLRuntimeException, SLRequestException, SLVersionException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("at/gv/egiz/bku/slcommands/infoboxreadcommand/IdentityLink.Binary.Invalid-2.xml"); assertNotNull(inputStream); diff --git a/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImplTest.java b/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImplTest.java index f10ca520..aa2bcd62 100644 --- a/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImplTest.java +++ b/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImplTest.java @@ -36,7 +36,7 @@ public class ErrorResultImplTest { ByteArrayOutputStream stream = new ByteArrayOutputStream(); StreamResult result = new StreamResult(stream); - errorResult.writeTo(result); + errorResult.writeTo(result, false); System.out.println(stream.toString()); diff --git a/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadComandImplTest.java b/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadComandImplTest.java index b0d11d47..bfc784f7 100644 --- a/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadComandImplTest.java +++ b/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadComandImplTest.java @@ -39,6 +39,7 @@ import at.gv.egiz.bku.slcommands.SLResult; import at.gv.egiz.bku.slexceptions.SLCommandException; import at.gv.egiz.bku.slexceptions.SLRequestException; import at.gv.egiz.bku.slexceptions.SLRuntimeException; +import at.gv.egiz.bku.slexceptions.SLVersionException; import at.gv.egiz.stal.STAL; import at.gv.egiz.stal.dummy.DummySTAL; @@ -63,7 +64,7 @@ public class InfoboxReadComandImplTest { } @Test - public void testInfboxReadRequest() throws SLCommandException, SLRuntimeException, SLRequestException { + public void testInfboxReadRequest() throws SLCommandException, SLRuntimeException, SLRequestException, SLVersionException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("at/gv/egiz/bku/slcommands/infoboxreadcommand/IdentityLink.Binary.xml"); assertNotNull(inputStream); @@ -73,11 +74,11 @@ public class InfoboxReadComandImplTest { assertTrue(command instanceof InfoboxReadCommand); SLResult result = command.execute(); - result.writeTo(new StreamResult(System.out)); + result.writeTo(new StreamResult(System.out), false); } @Test(expected=SLCommandException.class) - public void testInfboxReadRequestInvalid1() throws SLCommandException, SLRuntimeException, SLRequestException { + public void testInfboxReadRequestInvalid1() throws SLCommandException, SLRuntimeException, SLRequestException, SLVersionException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("at/gv/egiz/bku/slcommands/infoboxreadcommand/IdentityLink.Binary.Invalid-1.xml"); assertNotNull(inputStream); @@ -87,7 +88,7 @@ public class InfoboxReadComandImplTest { assertTrue(command instanceof InfoboxReadCommand); } - public void testInfboxReadRequestInvalid2() throws SLCommandException, SLRuntimeException, SLRequestException { + public void testInfboxReadRequestInvalid2() throws SLCommandException, SLRuntimeException, SLRequestException, SLVersionException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("at/gv/egiz/bku/slcommands/infoboxreadcommand/IdentityLink.Binary.Invalid-2.xml"); assertNotNull(inputStream); diff --git a/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/NullOperationResultImplTest.java b/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/NullOperationResultImplTest.java index 8632b67c..e9b0775f 100644 --- a/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/NullOperationResultImplTest.java +++ b/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/NullOperationResultImplTest.java @@ -33,7 +33,7 @@ public class NullOperationResultImplTest { ByteArrayOutputStream stream = new ByteArrayOutputStream(); StreamResult result = new StreamResult(stream); - nullOperationResult.writeTo(result); + nullOperationResult.writeTo(result, false); System.out.println(stream.toString()); diff --git a/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/SVPersonendatenInfoboxImplTest.java b/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/SVPersonendatenInfoboxImplTest.java index f9c60b86..a17f0797 100644 --- a/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/SVPersonendatenInfoboxImplTest.java +++ b/bkucommon/src/test/java/at/gv/egiz/bku/slcommands/impl/SVPersonendatenInfoboxImplTest.java @@ -23,7 +23,6 @@ import iaik.asn1.CodingException; import java.io.IOException; import java.io.InputStream; -import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; @@ -42,10 +41,12 @@ import at.gv.egiz.bku.slcommands.InfoboxReadCommand; import at.gv.egiz.bku.slcommands.SLCommand; import at.gv.egiz.bku.slcommands.SLCommandContext; import at.gv.egiz.bku.slcommands.SLCommandFactory; +import at.gv.egiz.bku.slcommands.SLMarshallerFactory; import at.gv.egiz.bku.slcommands.SLResult; import at.gv.egiz.bku.slexceptions.SLCommandException; import at.gv.egiz.bku.slexceptions.SLRequestException; import at.gv.egiz.bku.slexceptions.SLRuntimeException; +import at.gv.egiz.bku.slexceptions.SLVersionException; import at.gv.egiz.stal.STAL; import at.gv.egiz.stal.dummy.DummySTAL; @@ -93,9 +94,7 @@ public class SVPersonendatenInfoboxImplTest { JAXBElement ehic = new ObjectFactory().createEHIC(attributeList); - JAXBContext jaxbContext = SLCommandFactory.getInstance().getJaxbContext(); - - Marshaller marshaller = jaxbContext.createMarshaller(); + Marshaller marshaller = SLMarshallerFactory.getInstance().createMarshaller(false); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); @@ -105,7 +104,7 @@ public class SVPersonendatenInfoboxImplTest { @Ignore @Test - public void testInfboxReadRequest() throws SLCommandException, SLRuntimeException, SLRequestException { + public void testInfboxReadRequest() throws SLCommandException, SLRuntimeException, SLRequestException, SLVersionException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("at/gv/egiz/bku/slcommands/infoboxreadcommand/IdentityLink.Binary.xml"); assertNotNull(inputStream); @@ -115,12 +114,12 @@ public class SVPersonendatenInfoboxImplTest { assertTrue(command instanceof InfoboxReadCommand); SLResult result = command.execute(); - result.writeTo(new StreamResult(System.out)); + result.writeTo(new StreamResult(System.out), false); } @Ignore @Test(expected=SLCommandException.class) - public void testInfboxReadRequestInvalid1() throws SLCommandException, SLRuntimeException, SLRequestException { + public void testInfboxReadRequestInvalid1() throws SLCommandException, SLRuntimeException, SLRequestException, SLVersionException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("at/gv/egiz/bku/slcommands/infoboxreadcommand/IdentityLink.Binary.Invalid-1.xml"); assertNotNull(inputStream); @@ -131,7 +130,7 @@ public class SVPersonendatenInfoboxImplTest { } @Ignore - public void testInfboxReadRequestInvalid2() throws SLCommandException, SLRuntimeException, SLRequestException { + public void testInfboxReadRequestInvalid2() throws SLCommandException, SLRuntimeException, SLRequestException, SLVersionException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("at/gv/egiz/bku/slcommands/infoboxreadcommand/IdentityLink.Binary.Invalid-2.xml"); assertNotNull(inputStream); diff --git a/utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_1/TransformsInfoType.java b/utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_1/TransformsInfoType.java index 5ee40b95..e4a8f48e 100644 --- a/utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_1/TransformsInfoType.java +++ b/utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_1/TransformsInfoType.java @@ -24,11 +24,15 @@ package at.buergerkarte.namespaces.securitylayer._1; +import javax.xml.bind.JAXBElement; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAnyElement; import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementRef; import javax.xml.bind.annotation.XmlType; import org.w3._2000._09.xmldsig_.TransformsType; +import org.w3c.dom.Element; /** @@ -58,8 +62,9 @@ import org.w3._2000._09.xmldsig_.TransformsType; }) public class TransformsInfoType { - @XmlElement(name = "Transforms", namespace = "http://www.w3.org/2000/09/xmldsig#") - protected TransformsType transforms; + @XmlElementRef(name = "Transforms", namespace = "http://www.w3.org/2000/09/xmldsig#", type = JAXBElement.class) + @XmlAnyElement(lax = true) + protected Object transforms; @XmlElement(name = "FinalDataMetaInfo", required = true) protected MetaInfoType finalDataMetaInfo; @@ -68,10 +73,12 @@ public class TransformsInfoType { * * @return * possible object is - * {@link TransformsType } + * {@link JAXBElement }{@code <}{@link String }{@code >} + * {@link Object } + * {@link Element } * */ - public TransformsType getTransforms() { + public Object getTransforms() { return transforms; } @@ -80,10 +87,12 @@ public class TransformsInfoType { * * @param value * allowed object is - * {@link TransformsType } + * {@link JAXBElement }{@code <}{@link String }{@code >} + * {@link Object } + * {@link Element } * */ - public void setTransforms(TransformsType value) { + public void setTransforms(Object value) { this.transforms = value; } diff --git a/utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_20020225_/ErrorResponseType.java b/utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_20020225_/ErrorResponseType.java new file mode 100644 index 00000000..69b5cd9d --- /dev/null +++ b/utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_20020225_/ErrorResponseType.java @@ -0,0 +1,98 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.0.2-b01-fcs +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2009.09.07 at 09:47:31 AM CEST +// + + +package at.buergerkarte.namespaces.securitylayer._20020225_; + +import java.math.BigInteger; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlType; + + +/** + *

Java class for ErrorResponseType complex type. + * + *

The following schema fragment specifies the expected content contained within this class. + * + *

+ * <complexType name="ErrorResponseType">
+ *   <complexContent>
+ *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ *       <sequence>
+ *         <element name="ErrorCode" type="{http://www.w3.org/2001/XMLSchema}integer"/>
+ *         <element name="Info" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ *       </sequence>
+ *     </restriction>
+ *   </complexContent>
+ * </complexType>
+ * 
+ * + * + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "ErrorResponseType", propOrder = { + "errorCode", + "info" +}) +public class ErrorResponseType { + + @XmlElement(name = "ErrorCode", required = true) + protected BigInteger errorCode; + @XmlElement(name = "Info", required = true) + protected String info; + + /** + * Gets the value of the errorCode property. + * + * @return + * possible object is + * {@link BigInteger } + * + */ + public BigInteger getErrorCode() { + return errorCode; + } + + /** + * Sets the value of the errorCode property. + * + * @param value + * allowed object is + * {@link BigInteger } + * + */ + public void setErrorCode(BigInteger value) { + this.errorCode = value; + } + + /** + * Gets the value of the info property. + * + * @return + * possible object is + * {@link String } + * + */ + public String getInfo() { + return info; + } + + /** + * Sets the value of the info property. + * + * @param value + * allowed object is + * {@link String } + * + */ + public void setInfo(String value) { + this.info = value; + } + +} diff --git a/utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_20020225_/ObjectFactory.java b/utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_20020225_/ObjectFactory.java new file mode 100644 index 00000000..a02f9ca1 --- /dev/null +++ b/utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_20020225_/ObjectFactory.java @@ -0,0 +1,280 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.0.2-b01-fcs +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2009.09.07 at 09:47:31 AM CEST +// + + +package at.buergerkarte.namespaces.securitylayer._20020225_; + +import javax.xml.bind.JAXBElement; +import javax.xml.bind.annotation.XmlElementDecl; +import javax.xml.bind.annotation.XmlRegistry; +import javax.xml.namespace.QName; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the at.buergerkarte.namespaces.securitylayer._20020225_ package. + *

An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + private final static QName _CreateXMLSignatureRequest_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "CreateXMLSignatureRequest"); + private final static QName _InfoboxUpdateRequest_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "InfoboxUpdateRequest"); + private final static QName _ErrorResponse_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "ErrorResponse"); + private final static QName _VerifyXMLSignatureResponse_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "VerifyXMLSignatureResponse"); + private final static QName _CreateSessionKeyResponse_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "CreateSessionKeyResponse"); + private final static QName _GetPropertiesRequest_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "GetPropertiesRequest"); + private final static QName _GetPropertiesResponse_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "GetPropertiesResponse"); + private final static QName _InfoboxAvailableResponse_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "InfoboxAvailableResponse"); + private final static QName _InfoboxAvailableRequest_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "InfoboxAvailableRequest"); + private final static QName _CreateSessionKeyRequest_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "CreateSessionKeyRequest"); + private final static QName _InfoboxUpdateResponse_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "InfoboxUpdateResponse"); + private final static QName _CreateXMLSignatureResponse_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "CreateXMLSignatureResponse"); + private final static QName _GetStatusResponse_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "GetStatusResponse"); + private final static QName _CreateCMSSignatureRequest_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "CreateCMSSignatureRequest"); + private final static QName _CreateSymmetricSecretRequest_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "CreateSymmetricSecretRequest"); + private final static QName _VerifyXMLSignatureRequest_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "VerifyXMLSignatureRequest"); + private final static QName _CreateSymmetricSecretResponse_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "CreateSymmetricSecretResponse"); + private final static QName _CreateCMSSignatureResponse_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "CreateCMSSignatureResponse"); + private final static QName _VerifyCMSSignatureResponse_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "VerifyCMSSignatureResponse"); + private final static QName _InfoboxReadResponse_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "InfoboxReadResponse"); + private final static QName _VerifyCMSSignatureRequest_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "VerifyCMSSignatureRequest"); + private final static QName _InfoboxReadRequest_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "InfoboxReadRequest"); + private final static QName _GetStatusRequest_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "GetStatusRequest"); + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: at.buergerkarte.namespaces.securitylayer._20020225_ + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link ErrorResponseType } + * + */ + public ErrorResponseType createErrorResponseType() { + return new ErrorResponseType(); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "CreateXMLSignatureRequest") + public JAXBElement createCreateXMLSignatureRequest(Object value) { + return new JAXBElement(_CreateXMLSignatureRequest_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "InfoboxUpdateRequest") + public JAXBElement createInfoboxUpdateRequest(Object value) { + return new JAXBElement(_InfoboxUpdateRequest_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link ErrorResponseType }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "ErrorResponse") + public JAXBElement createErrorResponse(ErrorResponseType value) { + return new JAXBElement(_ErrorResponse_QNAME, ErrorResponseType.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "VerifyXMLSignatureResponse") + public JAXBElement createVerifyXMLSignatureResponse(Object value) { + return new JAXBElement(_VerifyXMLSignatureResponse_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "CreateSessionKeyResponse") + public JAXBElement createCreateSessionKeyResponse(Object value) { + return new JAXBElement(_CreateSessionKeyResponse_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "GetPropertiesRequest") + public JAXBElement createGetPropertiesRequest(Object value) { + return new JAXBElement(_GetPropertiesRequest_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "GetPropertiesResponse") + public JAXBElement createGetPropertiesResponse(Object value) { + return new JAXBElement(_GetPropertiesResponse_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "InfoboxAvailableResponse") + public JAXBElement createInfoboxAvailableResponse(Object value) { + return new JAXBElement(_InfoboxAvailableResponse_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "InfoboxAvailableRequest") + public JAXBElement createInfoboxAvailableRequest(Object value) { + return new JAXBElement(_InfoboxAvailableRequest_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "CreateSessionKeyRequest") + public JAXBElement createCreateSessionKeyRequest(Object value) { + return new JAXBElement(_CreateSessionKeyRequest_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "InfoboxUpdateResponse") + public JAXBElement createInfoboxUpdateResponse(Object value) { + return new JAXBElement(_InfoboxUpdateResponse_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "CreateXMLSignatureResponse") + public JAXBElement createCreateXMLSignatureResponse(Object value) { + return new JAXBElement(_CreateXMLSignatureResponse_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "GetStatusResponse") + public JAXBElement createGetStatusResponse(Object value) { + return new JAXBElement(_GetStatusResponse_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "CreateCMSSignatureRequest") + public JAXBElement createCreateCMSSignatureRequest(Object value) { + return new JAXBElement(_CreateCMSSignatureRequest_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "CreateSymmetricSecretRequest") + public JAXBElement createCreateSymmetricSecretRequest(Object value) { + return new JAXBElement(_CreateSymmetricSecretRequest_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "VerifyXMLSignatureRequest") + public JAXBElement createVerifyXMLSignatureRequest(Object value) { + return new JAXBElement(_VerifyXMLSignatureRequest_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "CreateSymmetricSecretResponse") + public JAXBElement createCreateSymmetricSecretResponse(Object value) { + return new JAXBElement(_CreateSymmetricSecretResponse_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "CreateCMSSignatureResponse") + public JAXBElement createCreateCMSSignatureResponse(Object value) { + return new JAXBElement(_CreateCMSSignatureResponse_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "VerifyCMSSignatureResponse") + public JAXBElement createVerifyCMSSignatureResponse(Object value) { + return new JAXBElement(_VerifyCMSSignatureResponse_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "InfoboxReadResponse") + public JAXBElement createInfoboxReadResponse(Object value) { + return new JAXBElement(_InfoboxReadResponse_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "VerifyCMSSignatureRequest") + public JAXBElement createVerifyCMSSignatureRequest(Object value) { + return new JAXBElement(_VerifyCMSSignatureRequest_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "InfoboxReadRequest") + public JAXBElement createInfoboxReadRequest(Object value) { + return new JAXBElement(_InfoboxReadRequest_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", name = "GetStatusRequest") + public JAXBElement createGetStatusRequest(Object value) { + return new JAXBElement(_GetStatusRequest_QNAME, Object.class, null, value); + } + +} diff --git a/utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_20020225_/package-info.java b/utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_20020225_/package-info.java new file mode 100644 index 00000000..084f6b11 --- /dev/null +++ b/utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_20020225_/package-info.java @@ -0,0 +1,9 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.0.2-b01-fcs +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2009.09.07 at 09:47:31 AM CEST +// + +@javax.xml.bind.annotation.XmlSchema(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020225#", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) +package at.buergerkarte.namespaces.securitylayer._20020225_; diff --git a/utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_20020831_/ObjectFactory.java b/utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_20020831_/ObjectFactory.java new file mode 100644 index 00000000..17f6d4b4 --- /dev/null +++ b/utils/src/main/java/at/buergerkarte/namespaces/securitylayer/_20020831_/ObjectFactory.java @@ -0,0 +1,112 @@ +// +// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.0.2-b01-fcs +// See http://java.sun.com/xml/jaxb +// Any modifications to this file will be lost upon recompilation of the source schema. +// Generated on: 2009.09.07 at 09:47:31 AM CEST +// + + +package at.buergerkarte.namespaces.securitylayer._20020831_; + +import javax.xml.bind.JAXBElement; +import javax.xml.bind.annotation.XmlElementDecl; +import javax.xml.bind.annotation.XmlRegistry; +import javax.xml.namespace.QName; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the at.buergerkarte.namespaces.securitylayer._20020831_ package. + *

An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + * + */ +@XmlRegistry +public class ObjectFactory { + + private final static QName _CreateXMLSignatureRequest_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020831#", "CreateXMLSignatureRequest"); + private final static QName _GetPropertiesResponse_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020831#", "GetPropertiesResponse"); + private final static QName _VerifyXMLSignatureResponse_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020831#", "VerifyXMLSignatureResponse"); + private final static QName _VerifyXMLSignatureRequest_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020831#", "VerifyXMLSignatureRequest"); + private final static QName _VerifyCMSSignatureResponse_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020831#", "VerifyCMSSignatureResponse"); + private final static QName _CreateXMLSignatureResponse_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020831#", "CreateXMLSignatureResponse"); + private final static QName _VerifyCMSSignatureRequest_QNAME = new QName("http://www.buergerkarte.at/namespaces/securitylayer/20020831#", "VerifyCMSSignatureRequest"); + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: at.buergerkarte.namespaces.securitylayer._20020831_ + * + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020831#", name = "CreateXMLSignatureRequest") + public JAXBElement createCreateXMLSignatureRequest(Object value) { + return new JAXBElement(_CreateXMLSignatureRequest_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020831#", name = "GetPropertiesResponse") + public JAXBElement createGetPropertiesResponse(Object value) { + return new JAXBElement(_GetPropertiesResponse_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020831#", name = "VerifyXMLSignatureResponse") + public JAXBElement createVerifyXMLSignatureResponse(Object value) { + return new JAXBElement(_VerifyXMLSignatureResponse_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020831#", name = "VerifyXMLSignatureRequest") + public JAXBElement createVerifyXMLSignatureRequest(Object value) { + return new JAXBElement(_VerifyXMLSignatureRequest_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020831#", name = "VerifyCMSSignatureResponse") + public JAXBElement createVerifyCMSSignatureResponse(Object value) { + return new JAXBElement(_VerifyCMSSignatureResponse_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020831#", name = "CreateXMLSignatureResponse") + public JAXBElement createCreateXMLSignatureResponse(Object value) { + return new JAXBElement(_CreateXMLSignatureResponse_QNAME, Object.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link Object }{@code >}} + * + */ + @XmlElementDecl(namespace = "http://www.buergerkarte.at/namespaces/securitylayer/20020831#", name = "VerifyCMSSignatureRequest") + public JAXBElement createVerifyCMSSignatureRequest(Object value) { + return new JAXBElement(_VerifyCMSSignatureRequest_QNAME, Object.class, null, value); + } + +} diff --git a/utils/src/main/java/at/gv/egiz/bku/utils/URLEncodingInputStream.java b/utils/src/main/java/at/gv/egiz/bku/utils/URLEncodingInputStream.java new file mode 100644 index 00000000..28ef6b88 --- /dev/null +++ b/utils/src/main/java/at/gv/egiz/bku/utils/URLEncodingInputStream.java @@ -0,0 +1,62 @@ +/** + * + */ +package at.gv.egiz.bku.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.CharBuffer; + +/** + * @author mcentner + * + */ +public class URLEncodingInputStream extends InputStream { + + private char[] buffer = new char[1]; + + private CharBuffer charBuffer = CharBuffer.wrap(buffer); + + protected Readable in; + + /** + * @param in + */ + public URLEncodingInputStream(Readable in) { + this.in = in; + } + + /* (non-Javadoc) + * @see java.io.InputStream#read() + */ + @Override + public int read() throws IOException { + charBuffer.rewind(); + if (in.read(charBuffer) == -1) { + return -1; + } + if (buffer[0] == '+') { + return ' '; + } else if (buffer[0] == '%') { + charBuffer.rewind(); + if (in.read(charBuffer) == -1) { + throw new IOException("Invalid URL encoding."); + } + int c1 = Character.digit(buffer[0], 16); + charBuffer.rewind(); + if (in.read(charBuffer) == -1) { + throw new IOException("Invalid URL encoding."); + } + int c2 = Character.digit(buffer[0], 16); + if (c1 == -1 || c2 == -1) { + throw new IOException("Invalid URL encoding."); + } + return ((c1 << 4) | c2); + } else { + return buffer[0]; + } + } + + + +} diff --git a/utils/src/main/java/at/gv/egiz/bku/utils/URLEncodingOutputStream.java b/utils/src/main/java/at/gv/egiz/bku/utils/URLEncodingOutputStream.java new file mode 100644 index 00000000..df42df6d --- /dev/null +++ b/utils/src/main/java/at/gv/egiz/bku/utils/URLEncodingOutputStream.java @@ -0,0 +1,134 @@ +/* +* 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.utils; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.CharBuffer; +import java.util.BitSet; + +/** + * An URLEncoding RFC3986, Section 2.1 + * OutputStream. + * + * @author mcentner + */ +public class URLEncodingOutputStream extends OutputStream { + + private static final int MAX_BUFFER_SIZE = 512; + + private static final BitSet UNRESERVED = new BitSet(256); + + static { + for (int i = '0'; i <= '9'; i++) { + UNRESERVED.set(i); + } + for (int i = 'a'; i <= 'z'; i++) { + UNRESERVED.set(i); + } + for (int i = 'A'; i <= 'Z'; i++) { + UNRESERVED.set(i); + } + UNRESERVED.set('-'); + UNRESERVED.set('_'); + UNRESERVED.set('.'); + UNRESERVED.set('*'); + UNRESERVED.set(' '); + } + + private static final char[] HEX = new char[] { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + private char[] buf; + + protected Appendable out; + + /** + * Creates a new instance of this URLEncodingOutputStream that writes to the + * given Appendable. + *

+ * Note: According to + * http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars the input + * for the {@link #write()} methods should be the UTF-8. + *

+ * + * @param out + */ + public URLEncodingOutputStream(Appendable out) { + this.out = out; + } + + /* (non-Javadoc) + * @see java.io.OutputStream#write(int) + */ + @Override + public void write(int b) throws IOException { + b &= 0xFF; + if (UNRESERVED.get(b)) { + if (b == ' ') { + out.append('+'); + } else { + out.append((char) b); + } + } else { + out.append('%').append(HEX[b >>> 4]).append(HEX[b & 0xF]); + } + + } + + /* (non-Javadoc) + * @see java.io.OutputStream#write(byte[], int, int) + */ + @Override + public void write(byte[] b, int off, int len) throws IOException { + + // ensure a buffer at least double the size of end - start + 1 + // but max + int sz = Math.min(len + 1, MAX_BUFFER_SIZE); + if (buf == null || buf.length < sz) { + buf = new char[sz]; + } + + int bPos = 0; + for (int i = 0; i < len; i++) { + if (bPos + 3 > buf.length) { + // flush buffer + out.append(CharBuffer.wrap(buf, 0, bPos)); + bPos = 0; + } + int c = 0xFF & b[off + i]; + if (UNRESERVED.get(c)) { + if (c == ' ') { + buf[bPos++] = '+'; + } else { + buf[bPos++] = (char) c; + } + } else { + buf[bPos++] = '%'; + buf[bPos++] = HEX[c >>> 4]; + buf[bPos++] = HEX[c & 0xF]; + } + } + out.append(CharBuffer.wrap(buf, 0, bPos)); + + } + + +} diff --git a/utils/src/main/java/at/gv/egiz/bku/utils/URLEncodingWriter.java b/utils/src/main/java/at/gv/egiz/bku/utils/URLEncodingWriter.java new file mode 100644 index 00000000..3ba90265 --- /dev/null +++ b/utils/src/main/java/at/gv/egiz/bku/utils/URLEncodingWriter.java @@ -0,0 +1,57 @@ +/* + * 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.utils; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.Charset; + +/** + * An URLEncoding RFC3986, Section + * 2.1 Writer, that uses an UTF-8 encoding according to http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars for + * writing non-ASCII characters. + * + * @author mcentner + */ +public class URLEncodingWriter extends Writer { + + protected OutputStreamWriter osw; + + public URLEncodingWriter(Appendable out) { + URLEncodingOutputStream urlEnc = new URLEncodingOutputStream(out); + osw = new OutputStreamWriter(urlEnc, Charset.forName("UTF-8")); + } + + @Override + public void close() throws IOException { + osw.close(); + } + + @Override + public void flush() throws IOException { + osw.flush(); + } + + @Override + public void write(char[] cbuf, int off, int len) throws IOException { + osw.write(cbuf, off, len); + } + +} diff --git a/utils/src/main/java/at/gv/egiz/marshal/MarshallerFactory.java b/utils/src/main/java/at/gv/egiz/marshal/MarshallerFactory.java index ccebcc81..3ac0a86e 100644 --- a/utils/src/main/java/at/gv/egiz/marshal/MarshallerFactory.java +++ b/utils/src/main/java/at/gv/egiz/marshal/MarshallerFactory.java @@ -31,13 +31,17 @@ public class MarshallerFactory { private static final Log log = LogFactory.getLog(MarshallerFactory.class); - public static Marshaller createMarshaller(JAXBContext ctx, boolean formattedOutput) throws JAXBException { + public static Marshaller createMarshaller(JAXBContext ctx, boolean formattedOutput, boolean fragment) throws JAXBException { Marshaller m = ctx.createMarshaller(); try { if (formattedOutput) { log.trace("setting marshaller property FORMATTED_OUTPUT"); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); } + if (fragment) { + log.trace("setting marshaller property FRAGMENT"); + m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE); + } log.trace("setting marshaller property NamespacePrefixMapper"); m.setProperty("com.sun.xml.bind.namespacePrefixMapper", new NamespacePrefixMapperImpl()); } catch (PropertyException ex) { @@ -45,8 +49,12 @@ public class MarshallerFactory { } return m; } + + public static Marshaller createMarshaller(JAXBContext ctx, boolean formattedOutput) throws JAXBException { + return createMarshaller(ctx, formattedOutput, false); + } public static Marshaller createMarshaller(JAXBContext ctx) throws JAXBException { - return createMarshaller(ctx, false); + return createMarshaller(ctx, false, false); } } diff --git a/utils/src/main/java/at/gv/egiz/marshal/NamespacePrefix.java b/utils/src/main/java/at/gv/egiz/marshal/NamespacePrefix.java deleted file mode 100644 index 3ae1d0ff..00000000 --- a/utils/src/main/java/at/gv/egiz/marshal/NamespacePrefix.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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.marshal; - -/** - * - * @author Clemens Orthacker - */ -public interface NamespacePrefix { - String CARDCHANNEL_PREFIX = "cc"; - String ECDSA_PREFIX = "ecdsa"; - String PERSONDATA_PREFIX = "pr"; - String SAML10_PREFIX = "saml"; - String SL_PREFIX = "sl"; - String XADES_PREFIX = "xades"; - String XMLDSIG_PREFIX = "dsig"; - String XSI_PREFIX = "xsi"; - -} diff --git a/utils/src/main/java/at/gv/egiz/marshal/NamespacePrefixMapperImpl.java b/utils/src/main/java/at/gv/egiz/marshal/NamespacePrefixMapperImpl.java index 519f6b1f..e0698977 100644 --- a/utils/src/main/java/at/gv/egiz/marshal/NamespacePrefixMapperImpl.java +++ b/utils/src/main/java/at/gv/egiz/marshal/NamespacePrefixMapperImpl.java @@ -17,6 +17,9 @@ package at.gv.egiz.marshal; //import com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper; +import java.util.HashMap; +import java.util.Map; + import com.sun.xml.bind.marshaller.NamespacePrefixMapper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -29,45 +32,32 @@ public class NamespacePrefixMapperImpl extends NamespacePrefixMapper { private static final Log log = LogFactory.getLog(NamespacePrefixMapperImpl.class); + protected static final Map prefixMap = new HashMap(); + + static { + prefixMap.put("http://www.w3.org/2001/XMLSchema-instance", "xsi"); + prefixMap.put("http://www.w3.org/2000/09/xmldsig#", "dsig"); + prefixMap.put("http://www.buergerkarte.at/namespaces/securitylayer/1.2#", "sl"); + prefixMap.put("http://www.buergerkarte.at/cardchannel", "cc"); + prefixMap.put("http://www.w3.org/2001/04/xmldsig-more#", "ecdsa"); + prefixMap.put("http://reference.e-government.gv.at/namespace/persondata/20020228#", "pr"); + prefixMap.put("urn:oasis:names:tc:SAML:1.0:assertion", "saml"); + prefixMap.put("http://uri.etsi.org/01903/v1.1.1#", "xades"); + prefixMap.put("http://www.buergerkarte.at/namespaces/securitylayer/20020225#", "sl10"); + prefixMap.put("http://www.buergerkarte.at/namespaces/securitylayer/20020831#", "sl11"); + } + + @Override public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) { if (log.isTraceEnabled()) { log.trace("prefix for namespace " + namespaceUri + " requested"); } - if ("http://www.w3.org/2001/XMLSchema-instance".equals(namespaceUri)) { - return NamespacePrefix.XSI_PREFIX; - } - - if ("http://www.w3.org/2000/09/xmldsig#".equals(namespaceUri)) { - return NamespacePrefix.XMLDSIG_PREFIX; - } - - if ("http://www.buergerkarte.at/namespaces/securitylayer/1.2#".equals(namespaceUri)) { - return NamespacePrefix.SL_PREFIX; - } - - if ("http://www.buergerkarte.at/cardchannel".equals(namespaceUri)) { - return NamespacePrefix.CARDCHANNEL_PREFIX; - } - - if ("http://www.w3.org/2001/04/xmldsig-more#".equals(namespaceUri)) { - return NamespacePrefix.ECDSA_PREFIX; - } - - if ("http://reference.e-government.gv.at/namespace/persondata/20020228#".equals(namespaceUri)) { - return NamespacePrefix.PERSONDATA_PREFIX; - } - - if ("urn:oasis:names:tc:SAML:1.0:assertion".equals(namespaceUri)) { - return NamespacePrefix.SAML10_PREFIX; - } - - if ("http://uri.etsi.org/01903/v1.1.1#".equals(namespaceUri)) { - return NamespacePrefix.XADES_PREFIX; - } - return suggestion; + String prefix = prefixMap.get(namespaceUri); + + return (prefix != null) ? prefix : suggestion; } /** diff --git a/utils/src/main/java/at/gv/egiz/validation/ReportingValidationEventHandler.java b/utils/src/main/java/at/gv/egiz/validation/ReportingValidationEventHandler.java new file mode 100644 index 00000000..6543c333 --- /dev/null +++ b/utils/src/main/java/at/gv/egiz/validation/ReportingValidationEventHandler.java @@ -0,0 +1,64 @@ +/* + * 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.validation; + +import javax.xml.bind.ValidationEvent; +import javax.xml.bind.ValidationEventHandler; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @author Clemens Orthacker + */ +public class ReportingValidationEventHandler implements ValidationEventHandler { + + protected static final Log log = LogFactory.getLog(ReportingValidationEventHandler.class); + + protected ValidationEvent errorEvent; + + /** + * + * @param event + * @return false, terminate the current unmarshal, validate, or marshal operation after handling this warning/error + * (except for WARNING validation events) + */ + @Override + public boolean handleEvent(ValidationEvent event) { + switch (event.getSeverity()) { + case ValidationEvent.WARNING: + log.info(event.getMessage()); + return true; + case ValidationEvent.ERROR: + log.warn(event.getMessage()); + errorEvent = event; + return false; + case ValidationEvent.FATAL_ERROR: + log.error(event.getMessage()); + errorEvent = event; + return false; + default: + log.debug(event.getMessage()); + return false; + } + } + + public ValidationEvent getErrorEvent() { + return errorEvent; + } + +} diff --git a/utils/src/main/java/at/gv/egiz/validation/ValidationEventLogger.java b/utils/src/main/java/at/gv/egiz/validation/ValidationEventLogger.java deleted file mode 100644 index 0fafdd7f..00000000 --- a/utils/src/main/java/at/gv/egiz/validation/ValidationEventLogger.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.validation; - -import javax.xml.bind.ValidationEvent; -import javax.xml.bind.ValidationEventHandler; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * - * @author Clemens Orthacker - */ -public class ValidationEventLogger implements ValidationEventHandler { - - protected static final Log log = LogFactory.getLog(ValidationEventLogger.class); - - /** - * - * @param event - * @return false, terminate the current unmarshal, validate, or marshal operation after handling this warning/error - * (except for WARNING validation events) - */ - @Override - public boolean handleEvent(ValidationEvent event) { - switch (event.getSeverity()) { - case ValidationEvent.WARNING: - log.info(event.getMessage()); - return true; - case ValidationEvent.ERROR: - log.warn(event.getMessage()); - return false; - case ValidationEvent.FATAL_ERROR: - log.error(event.getMessage()); - return false; - default: - log.debug(event.getMessage()); - return false; - } - } -} diff --git a/utils/src/main/java/at/gv/egiz/xades/QualifyingPropertiesFactory.java b/utils/src/main/java/at/gv/egiz/xades/QualifyingPropertiesFactory.java index 71ca1db9..82cba624 100644 --- a/utils/src/main/java/at/gv/egiz/xades/QualifyingPropertiesFactory.java +++ b/utils/src/main/java/at/gv/egiz/xades/QualifyingPropertiesFactory.java @@ -16,8 +16,6 @@ */ package at.gv.egiz.xades; -import at.gv.egiz.marshal.MarshallerFactory; -import at.gv.egiz.marshal.NamespacePrefixMapperImpl; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -51,6 +49,8 @@ import org.w3._2000._09.xmldsig_.DigestMethodType; import org.w3._2000._09.xmldsig_.X509IssuerSerialType; import org.w3c.dom.Node; +import at.gv.egiz.marshal.MarshallerFactory; + public class QualifyingPropertiesFactory { public static String NS_URI_V1_1_1 = "http://uri.etsi.org/01903/v1.1.1#"; @@ -155,7 +155,7 @@ public class QualifyingPropertiesFactory { return dataObjectFormatType; } - public JAXBElement createQualifyingProperties111(Date signingTime, List certificates, String idValue, List dataObjectFormats) throws QualifyingPropertiesException { + public JAXBElement createQualifyingProperties111(String target, Date signingTime, List certificates, String idValue, List dataObjectFormats) throws QualifyingPropertiesException { GregorianCalendar gregorianCalendar = new GregorianCalendar(); gregorianCalendar.setTimeZone(TimeZone.getTimeZone("UTC")); @@ -206,6 +206,8 @@ public class QualifyingPropertiesFactory { QualifyingPropertiesType qualifyingPropertiesType = qpFactory.createQualifyingPropertiesType(); qualifyingPropertiesType.setSignedProperties(signedPropertiesType); + qualifyingPropertiesType.setTarget(target); + return qpFactory.createQualifyingProperties(qualifyingPropertiesType); } diff --git a/utils/src/test/java/at/gv/egiz/bku/utils/URLEncodingOutputStreamTest.java b/utils/src/test/java/at/gv/egiz/bku/utils/URLEncodingOutputStreamTest.java new file mode 100644 index 00000000..e92b9584 --- /dev/null +++ b/utils/src/test/java/at/gv/egiz/bku/utils/URLEncodingOutputStreamTest.java @@ -0,0 +1,147 @@ +/* +* 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.utils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.StringWriter; +import java.io.Writer; +import java.net.URLEncoder; +import java.nio.charset.Charset; + +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + +public class URLEncodingOutputStreamTest { + + private static String buf; + + private static Charset UTF_8 = Charset.forName("UTF-8"); + + @BeforeClass + public static void setUpClass() throws IOException { + + ClassLoader cl = URLEncodingOutputStreamTest.class.getClassLoader(); + InputStream is = cl.getResourceAsStream("BigRequest.xml"); + + assertNotNull(is); + + InputStreamReader reader = new InputStreamReader(is, UTF_8); + + StringBuilder sb = new StringBuilder(); + + char[] b = new char[512]; + for (int l; (l = reader.read(b)) != -1;) { + sb.append(b, 0, l); + } + + buf = sb.toString(); + + } + + @Test + public void testCompareResults() throws IOException { + + String out1; + String out2; + + // new + StringWriter writer = new StringWriter(); + URLEncodingOutputStream urlEnc = new URLEncodingOutputStream(writer); + OutputStreamWriter streamWriter = new OutputStreamWriter(urlEnc, UTF_8); + streamWriter.append(buf); + streamWriter.flush(); + out1 = writer.toString(); + + // URLEncoder + out2 = URLEncoder.encode(buf, UTF_8.name()); + + for (int i = 0; i < out1.length(); i++) { + if (out1.charAt(i) != out2.charAt(i)) { + System.out.println(i + ": " + out1.substring(i)); + System.out.println(i + ": " + out2.substring(i)); + } + } + + assertEquals(out1, out2); + + } + + @Ignore + @Test + public void testURLEncodingOutputStream() throws IOException { + + NullWriter writer = new NullWriter(); + + URLEncodingOutputStream urlEnc = new URLEncodingOutputStream(writer); + OutputStreamWriter streamWriter = new OutputStreamWriter(urlEnc, UTF_8); + + long t0, t1, dt = 0; + for (int run = 0; run < 1000; run++) { + t0 = System.currentTimeMillis(); + streamWriter.append(buf); + t1 = System.currentTimeMillis(); + if (run > 1) { + dt += t1 - t0; + } + } + System.out.println("Time " + dt + "ms"); + + } + + @Ignore + @Test + public void testURLEncodingNaive() throws IOException { + + String in = new String(buf); + + long t0, t1, dt = 0; + for (int run = 0; run < 1000; run++) { + t0 = System.currentTimeMillis(); + URLEncoder.encode(in, "UTF-8"); + t1 = System.currentTimeMillis(); + if (run > 1) { + dt += t1 - t0; + } + } + System.out.println("Time (naive) " + dt + "ms"); + + } + + public class NullWriter extends Writer { + + @Override + public void close() throws IOException { + } + + @Override + public void flush() throws IOException { + } + + @Override + public void write(char[] cbuf, int off, int len) throws IOException { + } + + } + +} diff --git a/utils/src/test/resources/BigRequest.xml b/utils/src/test/resources/BigRequest.xml new file mode 100644 index 00000000..90eb1eb8 --- /dev/null +++ b/utils/src/test/resources/BigRequest.xml @@ -0,0 +1,1060 @@ + + +SecureSignatureKeypair + + + TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2Np +bmcgZWxpdC4gTnVsbGFtIHZ1bHB1dGF0ZSwgcmlzdXMgaW1wZXJkaWV0IGNvbnNl +cXVhdCB2YXJpdXMsIHJpc3VzIGR1aSB0ZW1wdXMgbGVvLCBub24gbGFjaW5pYSBl +bmltIG51bmMgYSBzZW0uIERvbmVjIHBvcnRhLCBpcHN1bSB0aW5jaWR1bnQgdWx0 +cmljZXMgaW50ZXJkdW0sIGZlbGlzIGF1Z3VlIHNvZGFsZXMgYW50ZSwgdmVsIG9y +bmFyZSBsaWJlcm8gbnVsbGEgZXQgcHVydXMuIENyYXMgdGVtcHVzIHZhcml1cyBw +b3J0YS4gRG9uZWMgaWQgcHVydXMgdXQgdmVsaXQgYmliZW5kdW0gbHVjdHVzIGJs +YW5kaXQgc2l0IGFtZXQgbmVxdWUuIENsYXNzIGFwdGVudCB0YWNpdGkgc29jaW9z +cXUgYWQgbGl0b3JhIHRvcnF1ZW50IHBlciBjb251YmlhIG5vc3RyYSwgcGVyIGlu +Y2VwdG9zIGhpbWVuYWVvcy4gQ3JhcyBmYWNpbGlzaXMgdGVtcHVzIGZlcm1lbnR1 +bS4gRG9uZWMgZmVybWVudHVtIG1hc3NhIGV0IHNhcGllbiBwb3N1ZXJlIHZlbmVu +YXRpcy4gSW4gcXVpcyB1cm5hIG9yY2kuIER1aXMgYSBsaWJlcm8gb3JjaS4gTnVs +bGEgcG9ydHRpdG9yIGF1Z3VlIHZpdGFlIGxpZ3VsYSB0ZW1wb3Igc2VkIHJ1dHJ1 +bSBtaSBjdXJzdXMuIEFlbmVhbiBpYWN1bGlzIG5pc2kgYXQgaXBzdW0gY29uc2Vj +dGV0dXIgZWdldCB2YXJpdXMgbWFnbmEgc29sbGljaXR1ZGluLiBVdCBmYWNpbGlz +aXMgdGVsbHVzIGEgbmVxdWUgZWdlc3RhcyBwbGFjZXJhdC4gRG9uZWMgdG9ydG9y +IHZlbGl0LCB0aW5jaWR1bnQgYSBtb2xsaXMgY3Vyc3VzLCBsdWN0dXMgYWxpcXVh +bSBsaWJlcm8uIER1aXMgbm9uIHRlbGx1cyBwcmV0aXVtIHRlbGx1cyB2YXJpdXMg +ZWxlaWZlbmQgaW4gc2VkIHNlbS4gU3VzcGVuZGlzc2UgZmVybWVudHVtIHRlbGx1 +cyBpZCBmZWxpcyB0ZW1wdXMgdml2ZXJyYS4gRG9uZWMgcG9ydHRpdG9yIHRpbmNp +ZHVudCBtYXVyaXMgbmVjIGV1aXNtb2QuCgpQcmFlc2VudCB1bGxhbWNvcnBlciB0 +cmlzdGlxdWUgbG9yZW0sIGV0IHBsYWNlcmF0IG51bmMgZWxlbWVudHVtIHV0LiBN +b3JiaSBldCBhZGlwaXNjaW5nIHNlbS4gTmFtIHZlbCBuaWJoIGV1IGxlbyBjb25k +aW1lbnR1bSByaG9uY3VzIHF1aXMgc2VkIHVybmEuIFBoYXNlbGx1cyBpZCBqdXN0 +byB1dCBvcmNpIHZlc3RpYnVsdW0gc2NlbGVyaXNxdWUuIE5hbSBtb2xsaXMgdG9y +dG9yIHB1cnVzLiBQZWxsZW50ZXNxdWUgaWFjdWxpcyBzZW1wZXIgbWFsZXN1YWRh +LiBJbnRlZ2VyIGJsYW5kaXQgZmVsaXMgYXQgbG9yZW0gZXVpc21vZCB2ZW5lbmF0 +aXMuIE1hZWNlbmFzIG5lYyBlbGVpZmVuZCBsZW8uIERvbmVjIGxvYm9ydGlzLCBy +aXN1cyBuZWMgdGVtcHVzIHZvbHV0cGF0LCBudWxsYSBlbGl0IGx1Y3R1cyBhcmN1 +LCBhYyBwb3N1ZXJlIGxhY3VzIG1hc3NhIGluIG1pLiBNYWVjZW5hcyBzaXQgYW1l +dCBudW5jIG5lYyB0ZWxsdXMgZWdlc3RhcyB2ZXN0aWJ1bHVtIHZpdGFlIGV0IGVs +aXQuIE51bGxhbSBjb25zZWN0ZXR1ciBydXRydW0gdGVsbHVzIGFjIGFjY3Vtc2Fu +LiBDdXJhYml0dXIgZ3JhdmlkYSBhdWd1ZSBldCBtZXR1cyBjdXJzdXMgZWxlaWZl +bmQuIENyYXMgb2RpbyBhcmN1LCB0aW5jaWR1bnQgdXQgZWdlc3RhcyB2ZWwsIGdy +YXZpZGEgaW4gZXJvcy4gTnVuYyBldCBtYWxlc3VhZGEgcXVhbS4gTWF1cmlzIGFj +IHRlbGx1cyBhcmN1LgoKQ3JhcyBuaXNsIHNhcGllbiwgdGluY2lkdW50IGVnZXQg +ZWxlaWZlbmQgdmVsLCBwcmV0aXVtIGlkIGxlY3R1cy4gQWxpcXVhbSB0b3J0b3Ig +dXJuYSwgcG9zdWVyZSBpZCBydXRydW0gbHVjdHVzLCBhdWN0b3Igc2VkIGFudGUu +IFByb2luIGZlcm1lbnR1bSwgbmliaCBhIHZvbHV0cGF0IHZvbHV0cGF0LCBlcm9z +IGRvbG9yIHNjZWxlcmlzcXVlIG1hZ25hLCB0ZW1wdXMgc29sbGljaXR1ZGluIGxp +YmVybyBkb2xvciBydXRydW0gZXN0LiBNYXVyaXMgcXVpcyBqdXN0byBhcmN1LiBQ +cm9pbiB2ZWhpY3VsYSBhZGlwaXNjaW5nIGVyb3Mgbm9uIGNvbmRpbWVudHVtLiBO +dW5jIHN1c2NpcGl0LCBsZWN0dXMgZXUgaWFjdWxpcyBtb2xlc3RpZSwgbWkgZG9s +b3IgcG9ydGEgZG9sb3IsIGV1IHNhZ2l0dGlzIG1hc3NhIGlwc3VtIGluIG1pLiBO +dW5jIHNlbXBlciBzY2VsZXJpc3F1ZSBsb3JlbSwgYSBzb2RhbGVzIHRvcnRvciBw +b3J0dGl0b3IgaWQuIE51bGxhIG1hdHRpcywgdG9ydG9yIG5lYyBpYWN1bGlzIHBy +ZXRpdW0sIGVyYXQgbGFjdXMgdnVscHV0YXRlIGR1aSwgdnVscHV0YXRlIGZhY2ls +aXNpcyBkb2xvciB0dXJwaXMgdXQgZmVsaXMuIE1hZWNlbmFzIHZpdGFlIHNlbSBl +dCBuaWJoIHNhZ2l0dGlzIHRpbmNpZHVudCBxdWlzIGluIHRvcnRvci4gQWxpcXVh +bSBpZCBzb2RhbGVzIHJpc3VzLiBJbnRlZ2VyIGZhY2lsaXNpcywgc2FwaWVuIHV0 +IHNhZ2l0dGlzIGNvbnNlY3RldHVyLCBkaWFtIGxvcmVtIHZpdmVycmEgZG9sb3Is +IHF1aXMgY29tbW9kbyBuaWJoIGVyYXQgcXVpcyBtZXR1cy4gUHJvaW4gZGljdHVt +IHJpc3VzIG1hdXJpcy4gTnVuYyBldSB1cm5hIHNpdCBhbWV0IHZlbGl0IHBsYWNl +cmF0IGVsZWlmZW5kLiBBZW5lYW4gc2l0IGFtZXQgcHVydXMgbnVuYy4gUHJvaW4g +bm9uIG5lcXVlIGEgdGVsbHVzIG1hdHRpcyBlZ2VzdGFzIGF0IHV0IG51bmMuIERv +bmVjIG5vbiBhbnRlIHZpdGFlIG9yY2kgcGVsbGVudGVzcXVlIHNjZWxlcmlzcXVl +LiBNYWVjZW5hcyBhYyBpYWN1bGlzIGZlbGlzLiBVdCBhZGlwaXNjaW5nIHN1c2Np +cGl0IGRpYW0gdXQgcG9ydGEuIERvbmVjIHZlc3RpYnVsdW0gbGFjaW5pYSBtYWdu +YSwgaWQgcnV0cnVtIG5pc2kgdmVuZW5hdGlzIHNlZC4KCk51bGxhIHNhZ2l0dGlz +IHBoYXJldHJhIGFudGUgZXUgb3JuYXJlLiBBbGlxdWFtIGV1IGRvbG9yIHV0IHVy +bmEgY29uZGltZW50dW0gcnV0cnVtLiBJbnRlZ2VyIHN1c2NpcGl0LCB2ZWxpdCBu +ZWMgc29sbGljaXR1ZGluIGN1cnN1cywgbGVjdHVzIHF1YW0gc2NlbGVyaXNxdWUg +bWksIGlkIGF1Y3RvciBzYXBpZW4gdG9ydG9yIHNlZCB0ZWxsdXMuIERvbmVjIHRp +bmNpZHVudCB0aW5jaWR1bnQgbGVjdHVzLCBxdWlzIHNvZGFsZXMgcHVydXMgaW1w +ZXJkaWV0IGV0LiBOdWxsYSBmYWNpbGlzaS4gTnVsbGFtIGlhY3VsaXMgZWxlbWVu +dHVtIGZlbGlzLCBlZ2V0IG1hdHRpcyBkb2xvciB0cmlzdGlxdWUgYWMuIFNlZCBl +Z2V0IHNlbSBuZXF1ZS4gVXQgZmVybWVudHVtLCBtaSBxdWlzIHZvbHV0cGF0IHZl +bmVuYXRpcywgbGFjdXMgbmVxdWUgY29udmFsbGlzIGVzdCwgdml0YWUgc3VzY2lw +aXQgdHVycGlzIGFudGUgYXQgcmlzdXMuIE51bmMgZmVybWVudHVtLCBtYWduYSBx +dWlzIHZlbmVuYXRpcyBldWlzbW9kLCBlbGl0IHZlbGl0IGZlcm1lbnR1bSBqdXN0 +bywgYSBkaWN0dW0gbGlndWxhIGp1c3RvIGFjIGxvcmVtLiBTZWQgZWdldCB0b3J0 +b3IgbWFnbmEsIHZpdmVycmEgaW50ZXJkdW0gbGVvLiBRdWlzcXVlIGluIGxhY3Vz +IGV0IGxlY3R1cyBhZGlwaXNjaW5nIGNvbnNlY3RldHVyIGluIHF1aXMgZXN0LiBN +YXVyaXMgZXQgZG9sb3IgZXQgbmVxdWUgbW9sZXN0aWUgY29uc2VjdGV0dXIgZXVp +c21vZCBldSBlbGl0LiBOdWxsYSBmYWNpbGlzaS4gUHJvaW4gYWMgdmVsaXQgaXBz +dW0sIHV0IHRyaXN0aXF1ZSBtYWduYS4gVmVzdGlidWx1bSBwb3N1ZXJlIG1hbGVz +dWFkYSBuaXNsIHNpdCBhbWV0IGFsaXF1YW0uIFN1c3BlbmRpc3NlIHNlZCBpcHN1 +bSBpZCB0ZWxsdXMgcG9ydGEgcG9zdWVyZSBlZ2V0IHNpdCBhbWV0IHR1cnBpcy4g +TnVuYyBhYyBkb2xvciB2ZWwgdXJuYSBkYXBpYnVzIGZlcm1lbnR1bSBuZWMgdmVo +aWN1bGEgdXJuYS4gRnVzY2UgdGluY2lkdW50IG1ldHVzIGV1IGlwc3VtIG1hdHRp +cyB0cmlzdGlxdWUuIFNlZCBmYXVjaWJ1cyBmcmluZ2lsbGEgYWRpcGlzY2luZy4g +Q3JhcyBwaGFyZXRyYSwgYW50ZSBzZWQgYWNjdW1zYW4gcmhvbmN1cywgZWxpdCBk +b2xvciBsYWNpbmlhIG1hdXJpcywgYXQgbGFvcmVldCBsaWJlcm8gYXVndWUgYXQg +bmlzbC4KClByb2luIGEgbGVvIHV0IHRvcnRvciBwb3J0YSBsdWN0dXMuIFZlc3Rp +YnVsdW0gYW50ZSBpcHN1bSBwcmltaXMgaW4gZmF1Y2lidXMgb3JjaSBsdWN0dXMg +ZXQgdWx0cmljZXMgcG9zdWVyZSBjdWJpbGlhIEN1cmFlOyBVdCBzZWQgbnVuYyB2 +ZWwgbWV0dXMgc3VzY2lwaXQgY29uZ3VlIGF0IGEgZG9sb3IuIE51bmMgZXUgdG9y +dG9yIGxvcmVtLCBuZWMgY29uZ3VlIGxpYmVyby4gTW9yYmkgbGFvcmVldCBsZWN0 +dXMgbmlzbCwgdml0YWUgcmhvbmN1cyBlbGl0LiBTdXNwZW5kaXNzZSBldCBxdWFt +IHF1aXMgZHVpIGxhY2luaWEgc29kYWxlcyBuZWMgZXUgbGlndWxhLiBQZWxsZW50 +ZXNxdWUgbm9uIGlwc3VtIGxlbywgc2l0IGFtZXQgbW9sZXN0aWUgZHVpLiBTZWQg +YSBhdWd1ZSBlZ2V0IG1hdXJpcyBncmF2aWRhIG1hbGVzdWFkYSBldCBxdWlzIGlw +c3VtLiBBbGlxdWFtIGFjIG5pYmggbGlndWxhLCBpbiBwb3J0dGl0b3IgZWxpdC4g +TWF1cmlzIHR1cnBpcyBvcmNpLCBhY2N1bXNhbiBpbiBpbnRlcmR1bSBhdCwgZmFj +aWxpc2lzIHZpdGFlIHR1cnBpcy4gUXVpc3F1ZSBtYXR0aXMgcGVsbGVudGVzcXVl +IGVyb3MgdmVsIHZpdmVycmEuIFZlc3RpYnVsdW0gbGFvcmVldCBjb25ndWUgYWxp +cXVhbS4gRG9uZWMgcG9zdWVyZSBtYXVyaXMgbmVjIGxpYmVybyBvcm5hcmUgZXQg +ZWdlc3RhcyBhbnRlIHB1bHZpbmFyLiBDdXJhYml0dXIgYW50ZSBhbnRlLCBtb2xl +c3RpZSB1dCBiaWJlbmR1bSB2aXRhZSwgcnV0cnVtIHZlbCByaXN1cy4gRG9uZWMg +ZXQgbmVxdWUgcHVydXMsIHNpdCBhbWV0IGFkaXBpc2NpbmcgZmVsaXMuIFBlbGxl +bnRlc3F1ZSBkaWduaXNzaW0gdmVzdGlidWx1bSBzYXBpZW4gbmVjIGZyaW5naWxs +YS4gUHJvaW4gbmVjIHB1cnVzIGV0IGVzdCBldWlzbW9kIHBoYXJldHJhLiBQZWxs +ZW50ZXNxdWUgZGFwaWJ1cyBkYXBpYnVzIG1ldHVzIHZlbCBmYWNpbGlzaXMuIFZp +dmFtdXMgdmVsIGVsaXQgbnVuYy4KCkV0aWFtIGFjIGVuaW0gZWdldCBtYXVyaXMg +ZmF1Y2lidXMgZGFwaWJ1cy4gTW9yYmkgdml0YWUgbGVjdHVzIG5lcXVlLiBNYXVy +aXMgc2FwaWVuIG1ldHVzLCBzdXNjaXBpdCBzaXQgYW1ldCBlZ2VzdGFzIHZpdGFl +LCBwaGFyZXRyYSBhIG1hc3NhLiBVdCB2YXJpdXMsIHRvcnRvciBzZWQgZnJpbmdp +bGxhIHBsYWNlcmF0LCBuZXF1ZSBkaWFtIGNvbmd1ZSBudW5jLCBpZCBhbGlxdWV0 +IG5pc2kgcmlzdXMgdXQgdXJuYS4gVml2YW11cyBwbGFjZXJhdCBwb3J0YSBhcmN1 +IHZpdGFlIGFjY3Vtc2FuLiBNYXVyaXMgaGVuZHJlcml0LCBlbmltIHZpdGFlIGFs +aXF1YW0gcG9ydHRpdG9yLCBwdXJ1cyBuZXF1ZSBncmF2aWRhIG1hZ25hLCBub24g +cHJldGl1bSBuZXF1ZSBsZWN0dXMgc2l0IGFtZXQgdG9ydG9yLiBEdWlzIHN1c2Np +cGl0IG9ybmFyZSBvZGlvIHZpdGFlIHBoYXJldHJhLiBEb25lYyBlbGVpZmVuZCwg +ZHVpIG5vbiBncmF2aWRhIGNvbnZhbGxpcywgb2RpbyBhcmN1IGJsYW5kaXQgbWFn +bmEsIG5vbiBwb3J0YSBsZW8gbGliZXJvIHBvcnRhIG1hZ25hLiBEdWlzIGVzdCB1 +cm5hLCBsdWN0dXMgZXUgaW50ZXJkdW0gbmVjLCBpYWN1bGlzIG5lYyBlbGl0LiBD +dXJhYml0dXIgZmFjaWxpc2lzIHRlbXB1cyB0ZW1wdXMuIFNlZCBzZW0gdXJuYSwg +dml2ZXJyYSBldSBpbXBlcmRpZXQgc2l0IGFtZXQsIGludGVyZHVtIGV0IGp1c3Rv +LiBJbiBoYWMgaGFiaXRhc3NlIHBsYXRlYSBkaWN0dW1zdC4gRG9uZWMgdGluY2lk +dW50IG1hc3NhIHV0IGR1aSBmZXJtZW50dW0gcG9zdWVyZSBldCBuZWMgdHVycGlz +LiBFdGlhbSBmZXJtZW50dW0gcG9ydGEgbWF1cmlzLiBTdXNwZW5kaXNzZSBsYWN1 +cyBsaWJlcm8sIHByZXRpdW0gaW4gZWdlc3RhcyB2ZWwsIHNhZ2l0dGlzIGV0IG1h +c3NhLiBOdWxsYSBhbGlxdWFtIGxhb3JlZXQgc2FwaWVuLCBhdCBwZWxsZW50ZXNx +dWUgZXJvcyB2ZW5lbmF0aXMgcXVpcy4gSW50ZWdlciBhY2N1bXNhbiwgbGFjdXMg +dXQgZGFwaWJ1cyBlZ2VzdGFzLCByaXN1cyBuaXNsIHNlbXBlciB0dXJwaXMsIHNp +dCBhbWV0IHZpdmVycmEgZmVsaXMgdHVycGlzIHNlZCB2ZWxpdC4KCkRvbmVjIHZl +aGljdWxhLCB0ZWxsdXMgcXVpcyBtb2xlc3RpZSBiaWJlbmR1bSwgYXVndWUgbmlz +bCB0ZW1wdXMgbG9yZW0sIHNpdCBhbWV0IGNvbmRpbWVudHVtIGp1c3RvIGF1Z3Vl +IHNpdCBhbWV0IGFudGUuIE51bGxhbSB0b3J0b3Igc2VtLCBtYXR0aXMgYWMgcG9y +dHRpdG9yIHZpdGFlLCBpYWN1bGlzIHNlZCBkdWkuIEN1bSBzb2NpaXMgbmF0b3F1 +ZSBwZW5hdGlidXMgZXQgbWFnbmlzIGRpcyBwYXJ0dXJpZW50IG1vbnRlcywgbmFz +Y2V0dXIgcmlkaWN1bHVzIG11cy4gRXRpYW0gaW4gc2VtIGlwc3VtLiBJbiBldSBt +b2xlc3RpZSBtZXR1cy4gVml2YW11cyBsYW9yZWV0IGZhdWNpYnVzIG5pYmgsIGEg +c2VtcGVyIG5pYmggZnJpbmdpbGxhIHV0LiBNYWVjZW5hcyBsYW9yZWV0LCBwdXJ1 +cyBldSBzb2xsaWNpdHVkaW4gcGhhcmV0cmEsIGp1c3RvIHRlbGx1cyBjb21tb2Rv +IGF1Z3VlLCBpbiBmYXVjaWJ1cyBsZW8ganVzdG8gbmVjIG5pc2wuIE5hbSBldSBw +cmV0aXVtIGVzdC4gRnVzY2Ugc2l0IGFtZXQgcXVhbSBsb3JlbSwgdXQgc29kYWxl +cyB0ZWxsdXMuIE51bmMgZWxlbWVudHVtLCBzZW0gc2NlbGVyaXNxdWUgaWFjdWxp +cyBpbnRlcmR1bSwgZXJvcyBkdWkgb3JuYXJlIG9yY2ksIHNlZCBwbGFjZXJhdCBy +aXN1cyBlcmF0IGVnZXQgYXJjdS4gTW9yYmkgYWMgbWV0dXMgaWQgbGlndWxhIHBv +cnRhIGdyYXZpZGEuIE51bmMgY29udmFsbGlzIGRpYW0gaW4gbmlzaSBncmF2aWRh +IGFjIGNvbW1vZG8gbGFjdXMgZGlnbmlzc2ltLiBDdW0gc29jaWlzIG5hdG9xdWUg +cGVuYXRpYnVzIGV0IG1hZ25pcyBkaXMgcGFydHVyaWVudCBtb250ZXMsIG5hc2Nl +dHVyIHJpZGljdWx1cyBtdXMuIEV0aWFtIHF1aXMgdmVsaXQgZXUgZmVsaXMgbHVj +dHVzIHBlbGxlbnRlc3F1ZSBub24gZWdlc3RhcyBuZXF1ZS4gTnVuYyBuaWJoIHRl +bGx1cywgcGxhY2VyYXQgbm9uIGZlcm1lbnR1bSBhdCwgb3JuYXJlIHF1aXMgZGlh +bS4gU3VzcGVuZGlzc2Ugc2l0IGFtZXQgcHVydXMgcXVpcyBkdWkgY29uc2VxdWF0 +IHByZXRpdW0uIEZ1c2NlIGV0IG1hZ25hIG5pc2ksIHZlbCB2ZXN0aWJ1bHVtIGVy +b3MuIEluIGZhdWNpYnVzLCBsYWN1cyBlZ2V0IGZhdWNpYnVzIGZldWdpYXQsIGxp +Z3VsYSBlbmltIHZlc3RpYnVsdW0gbG9yZW0sIGEgdml2ZXJyYSBsb3JlbSBlc3Qg +dml0YWUgcXVhbS4gTnVuYyBwdXJ1cyBuaXNsLCB2YXJpdXMgZXQgdGVtcG9yIHVs +dHJpY2llcywgaWFjdWxpcyBhIGxpYmVyby4gQ2xhc3MgYXB0ZW50IHRhY2l0aSBz +b2Npb3NxdSBhZCBsaXRvcmEgdG9ycXVlbnQgcGVyIGNvbnViaWEgbm9zdHJhLCBw +ZXIgaW5jZXB0b3MgaGltZW5hZW9zLgoKRnVzY2UgdmVsIGp1c3RvIHNpdCBhbWV0 +IHF1YW0gbWFsZXN1YWRhIG9ybmFyZSBzaXQgYW1ldCBldCBhbnRlLiBOdWxsYW0g +cmhvbmN1cyBwb3J0YSBzZW0gcXVpcyBtYWxlc3VhZGEuIENsYXNzIGFwdGVudCB0 +YWNpdGkgc29jaW9zcXUgYWQgbGl0b3JhIHRvcnF1ZW50IHBlciBjb251YmlhIG5v +c3RyYSwgcGVyIGluY2VwdG9zIGhpbWVuYWVvcy4gRG9uZWMgYW50ZSBvZGlvLCB0 +aW5jaWR1bnQgYWMgbW9sbGlzIGluLCB1bHRyaWNlcyBuZWMgZHVpLiBOdW5jIGN1 +cnN1cyBtYWduYSBuZWMgcHVydXMgZnJpbmdpbGxhIGEgbW9sZXN0aWUgcHVydXMg +bG9ib3J0aXMuIEZ1c2NlIHJ1dHJ1bSwgbG9yZW0gZWdldCB1bGxhbWNvcnBlciBm +cmluZ2lsbGEsIGR1aSBsaWd1bGEgc29sbGljaXR1ZGluIHJpc3VzLCB2aXRhZSBw +b3N1ZXJlIG5lcXVlIGZlbGlzIHZpdGFlIHF1YW0uIEN1cmFiaXR1ciBjb25kaW1l +bnR1bSBsaWJlcm8gYXQgb3JjaSBhdWN0b3IgZXUgY29tbW9kbyBsaWJlcm8gc29s +bGljaXR1ZGluLiBDcmFzIHNlZCBwdXJ1cyBtaSwgc2VkIGNvbnZhbGxpcyBuaWJo +LiBOdWxsYW0gYWMgbG9yZW0gYSBpcHN1bSBsYWNpbmlhIHVsbGFtY29ycGVyIGlk +IHF1aXMgdG9ydG9yLiBNb3JiaSBlbGVpZmVuZCwgbnVsbGEgdmVsIHZlbmVuYXRp +cyBjb25kaW1lbnR1bSwgbGVjdHVzIHRvcnRvciB2ZW5lbmF0aXMgcmlzdXMsIGEg +bHVjdHVzIGxhY3VzIGlwc3VtIHV0IHNhcGllbi4KCk1hdXJpcyBsYW9yZWV0IG51 +bmMgc2l0IGFtZXQgZW5pbSBkaWN0dW0gbG9ib3J0aXMuIE1hdXJpcyBydXRydW0s +IGVsaXQgdXQgbW9sZXN0aWUgb3JuYXJlLCB0b3J0b3IgZXJvcyB2YXJpdXMgbnVu +YywgdHJpc3RpcXVlIHBlbGxlbnRlc3F1ZSB0dXJwaXMgYXVndWUgaW4gYW50ZS4g +QWVuZWFuIGZlbGlzIGR1aSwgZnJpbmdpbGxhIGluIGFsaXF1YW0gYXQsIG9ybmFy +ZSB1dCBudWxsYS4gSW4gbWkgbnVuYywgc2VtcGVyIGluIGNvbmRpbWVudHVtIG5v +biwgY29uZ3VlIGFjIG1hdXJpcy4gQ3VyYWJpdHVyIGltcGVyZGlldCByaG9uY3Vz +IHNlbSwgbW9sZXN0aWUgYWRpcGlzY2luZyBpcHN1bSBzb2xsaWNpdHVkaW4gYS4g +Vml2YW11cyB1dCBpcHN1bSBxdWlzIHR1cnBpcyBhbGlxdWV0IHRpbmNpZHVudCBh +Y2N1bXNhbiBhYyBxdWFtLiBDdXJhYml0dXIgcG9ydHRpdG9yLCBtYXVyaXMgYWMg +bHVjdHVzIHZpdmVycmEsIHB1cnVzIGVzdCBhZGlwaXNjaW5nIHNhcGllbiwgc2l0 +IGFtZXQgYWxpcXVhbSBxdWFtIG51bGxhIGF0IGVyYXQuIFZpdmFtdXMgdGVtcHVz +LCBqdXN0byBhIHByZXRpdW0gZGljdHVtLCBpcHN1bSBudW5jIGxhb3JlZXQgdGVs +bHVzLCBuZWMgdGVtcHVzIHNlbSBuaXNpIGFjIHF1YW0uIFZpdmFtdXMgaW4gbWFz +c2EgZW5pbS4gUXVpc3F1ZSBuZWMgdG9ydG9yIHZpdGFlIG51bGxhIHVsdHJpY2Vz +IGNvbnZhbGxpcy4gTnVsbGFtIHRvcnRvciBtYXVyaXMsIGF1Y3RvciB2aXRhZSB0 +ZW1wb3IgZXQsIGF1Y3RvciBuZWMgc2FwaWVuLiBRdWlzcXVlIHVsbGFtY29ycGVy +IHZpdmVycmEgdmVuZW5hdGlzLiBNYWVjZW5hcyBxdWlzIGdyYXZpZGEgbWFzc2Eu +IEludGVnZXIgdml0YWUganVzdG8gbGVjdHVzLCBhYyBtYWxlc3VhZGEgcXVhbS4g +TWFlY2VuYXMgc2l0IGFtZXQgbmVxdWUgbnVsbGEsIG5lYyBpbXBlcmRpZXQgaXBz +dW0uIFNlZCB0ZW1wdXMgYmliZW5kdW0gc2FwaWVuLCBub24gZnJpbmdpbGxhIG51 +bmMgZWxlbWVudHVtIGV1LiBOdW5jIGF1Y3RvciBhbGlxdWV0IGxlbywgYmliZW5k +dW0gcHJldGl1bSBkaWFtIHBsYWNlcmF0IGFjLiBOYW0gaW4gZW5pbSBkdWkuIFNl +ZCBldCBuaWJoIG5vbiBudW5jIHBsYWNlcmF0IHBoYXJldHJhLiBTZWQgb2RpbyBs +ZW8sIGNvbmRpbWVudHVtIGV1IHN1c2NpcGl0IGV1LCBtb2xsaXMgZWdldCBuaXNp +LgoKUHJvaW4gb3JuYXJlLCBpcHN1bSB2aXRhZSBsYW9yZWV0IHZhcml1cywgbGFj +dXMgbGVvIHBoYXJldHJhIG1hdXJpcywgc2VkIGNvbnZhbGxpcyBsaWJlcm8gbWV0 +dXMgcmhvbmN1cyBvcmNpLiBNYXVyaXMgcnV0cnVtIGxlbyB2ZWwgYW50ZSBlZ2Vz +dGFzIGEgYWRpcGlzY2luZyBlc3QgdmVuZW5hdGlzLiBJbiBldSBtaSB1dCBlbGl0 +IHRyaXN0aXF1ZSB2ZWhpY3VsYS4gTnVuYyBwb3N1ZXJlLCBlbmltIHF1aXMgc3Vz +Y2lwaXQgYWNjdW1zYW4sIGVsaXQgbGVvIHNlbXBlciBudW5jLCBub24gbGFvcmVl +dCBhbnRlIG5pYmggc2l0IGFtZXQgbWF1cmlzLiBBbGlxdWFtIGFjIHF1YW0gcXVp +cyBuaXNsIHNvbGxpY2l0dWRpbiBtb2xlc3RpZSBpZCBhYyBudWxsYS4gSW50ZWdl +ciBldSBkb2xvciBpcHN1bS4gUGhhc2VsbHVzIGludGVyZHVtIHZlaGljdWxhIHNl +bXBlci4gU3VzcGVuZGlzc2UgbmVjIGFyY3UgYWMgZXN0IGlhY3VsaXMgdmVoaWN1 +bGEuIE1hZWNlbmFzIHNlbXBlciBsaWJlcm8gaWFjdWxpcyBsb3JlbSBmZXVnaWF0 +IGF1Y3Rvci4gQWxpcXVhbSBibGFuZGl0IHNlbXBlciBibGFuZGl0LiBVdCBlZ2Vz +dGFzIGVyb3Mgc2VkIG5pc2kgZGFwaWJ1cyBhIHNlbXBlciBtZXR1cyBkYXBpYnVz +LiBNb3JiaSB2dWxwdXRhdGUgbGFvcmVldCB2ZWxpdCwgZXUgYmliZW5kdW0gYXJj +dSBjb21tb2RvIG5vbi4gTWFlY2VuYXMgZmVsaXMgbnVuYywgdm9sdXRwYXQgaWQg +dmVzdGlidWx1bSB2aXRhZSwgdmVzdGlidWx1bSB2aXRhZSBhbnRlLgoKU2VkIG51 +bmMgaXBzdW0sIGF1Y3RvciB2ZWwgZmV1Z2lhdCBzaXQgYW1ldCwgaW50ZXJkdW0g +YXQgbGlndWxhLiBNYXVyaXMgcXVpcyBuaXNpIGVnZXQgbGVjdHVzIGdyYXZpZGEg +c2VtcGVyIGluIHF1aXMgZW5pbS4gTnVsbGEgYXVjdG9yIGVsZW1lbnR1bSBqdXN0 +bywgbm9uIGJpYmVuZHVtIGR1aSBmZXJtZW50dW0gYWMuIEZ1c2NlIGxvcmVtIGF1 +Z3VlLCBjb21tb2RvIG5vbiB0aW5jaWR1bnQgc2l0IGFtZXQsIGFjY3Vtc2FuIHNl +ZCBtZXR1cy4gTmFtIHNhcGllbiBlbmltLCBkaWduaXNzaW0gZXQgdmFyaXVzIGV1 +LCBsdWN0dXMgYXQgb3JjaS4gTnVsbGFtIGxpYmVybyBvcmNpLCBiaWJlbmR1bSBh +IGxhY2luaWEgYWMsIHZ1bHB1dGF0ZSBmYXVjaWJ1cyBtaS4gTW9yYmkgY29uZGlt +ZW50dW0gZmVybWVudHVtIGRpZ25pc3NpbS4gTG9yZW0gaXBzdW0gZG9sb3Igc2l0 +IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gUXVpc3F1ZSBldCBu +ZXF1ZSBlcmF0LCB1dCB2b2x1dHBhdCBsZW8uIFZlc3RpYnVsdW0gdmVsIGxpZ3Vs +YSBuZXF1ZS4gTnVuYyBwaGFyZXRyYSBsaWJlcm8gaW4gbWF1cmlzIGRhcGlidXMg +aW4gbGFvcmVldCBkdWkgbHVjdHVzLiBQaGFzZWxsdXMgdmVsaXQgbmlzaSwgZGlj +dHVtIGF0IHZvbHV0cGF0IG5lYywgaGVuZHJlcml0IGZhY2lsaXNpcyBlc3QuIFN1 +c3BlbmRpc3NlIG1hdXJpcyBwdXJ1cywgZGlnbmlzc2ltIHNpdCBhbWV0IHRpbmNp +ZHVudCBhYywgaWFjdWxpcyBpZCBzZW0uIER1aXMgcmlzdXMganVzdG8sIGZyaW5n +aWxsYSBhdCB2dWxwdXRhdGUgYXQsIGxvYm9ydGlzIG5vbiBhcmN1LiBBbGlxdWFt +IGVyYXQgdm9sdXRwYXQuCgpWaXZhbXVzIGxlY3R1cyBtaSwgdWxsYW1jb3JwZXIg +ZXQgZmV1Z2lhdCBzaXQgYW1ldCwgZ3JhdmlkYSBxdWlzIHZlbGl0LiBDcmFzIHRp +bmNpZHVudCBtZXR1cyByaXN1cywgZWdldCB0cmlzdGlxdWUgYXJjdS4gTnVuYyBl +Z2V0IGxlbyBhIGVsaXQgdGVtcG9yIHBvcnR0aXRvci4gQ3VtIHNvY2lpcyBuYXRv +cXVlIHBlbmF0aWJ1cyBldCBtYWduaXMgZGlzIHBhcnR1cmllbnQgbW9udGVzLCBu +YXNjZXR1ciByaWRpY3VsdXMgbXVzLiBQcmFlc2VudCB0ZW1wdXMgbnVuYyBlZ2V0 +IG5pYmggY29uc2VjdGV0dXIgYWMgY29udmFsbGlzIGVuaW0gc2FnaXR0aXMuIEFs +aXF1YW0gaXBzdW0gdXJuYSwgZmF1Y2lidXMgYWMgdHJpc3RpcXVlIHZlbCwgbGFv +cmVldCBhIGxlby4gUGhhc2VsbHVzIGNvbmRpbWVudHVtIGVuaW0gZWdldCBpcHN1 +bSB2b2x1dHBhdCB1dCBhY2N1bXNhbiBuaWJoIHBlbGxlbnRlc3F1ZS4gTnVsbGFt +IGxhb3JlZXQsIHRvcnRvciBpbiB1bGxhbWNvcnBlciBmcmluZ2lsbGEsIGR1aSBv +ZGlvIHNjZWxlcmlzcXVlIHB1cnVzLCB1dCBpYWN1bGlzIGVyYXQgcmlzdXMgYWMg +bnVsbGEuIEFsaXF1YW0gZWdlc3RhcyBsYWNpbmlhIGFsaXF1YW0uIE1hdXJpcyBi +bGFuZGl0LCB0b3J0b3IgcXVpcyBtb2xsaXMgcGVsbGVudGVzcXVlLCBudWxsYSBt +YWduYSB2ZXN0aWJ1bHVtIGVyYXQsIGV0IHRyaXN0aXF1ZSBsaWd1bGEgc2VtIGVn +ZXQgdGVsbHVzLiBWZXN0aWJ1bHVtIGZlbGlzIHRvcnRvciwgc2VtcGVyIGluIHN1 +c2NpcGl0IGVnZXQsIHRpbmNpZHVudCB2ZWwgdmVsaXQuCgpNYWVjZW5hcyBzZW0g +dGVsbHVzLCBiaWJlbmR1bSBhYyBjdXJzdXMgdXQsIGZldWdpYXQgdmVsIHRvcnRv +ci4gU2VkIHZlbmVuYXRpcyBmZWxpcyBhIGF1Z3VlIHByZXRpdW0gZXUgbGFvcmVl +dCBkdWkgbWF0dGlzLiBNYWVjZW5hcyByaG9uY3VzIHZlc3RpYnVsdW0gbWFnbmEg +ZWdldCBjb252YWxsaXMuIE51bGxhIGZhY2lsaXNpLiBGdXNjZSBpbnRlcmR1bSBk +aWN0dW0gbGVvIG5lYyBhZGlwaXNjaW5nLiBOdW5jIHZpdGFlIGxvcmVtIHF1YW0u +IEluIHVsdHJpY2llcyBzZW0gZXUgbGlndWxhIGVsZWlmZW5kIGluIHNhZ2l0dGlz +IGFyY3UgcnV0cnVtLiBOdWxsYW0gZGlhbSBqdXN0bywgZmVybWVudHVtIG5lYyBs +dWN0dXMgZXUsIGltcGVyZGlldCBuZWMgbGliZXJvLiBJbnRlZ2VyIGhlbmRyZXJp +dCB0ZW1wb3IgZGFwaWJ1cy4gU3VzcGVuZGlzc2UgcG90ZW50aS4gRG9uZWMgdXQg +YXJjdSBuZWMgbG9yZW0gbHVjdHVzIHZhcml1cyB2aXRhZSBhIG51bmMuIEZ1c2Nl +IGEgc2FwaWVuIGxhb3JlZXQgdGVsbHVzIGFkaXBpc2NpbmcgdmVzdGlidWx1bS4g +QWxpcXVhbSB2YXJpdXMgZGljdHVtIG1pIGVnZXQgZmV1Z2lhdC4gRXRpYW0gc2Vk +IGxlbyBldCBtYXVyaXMgZmV1Z2lhdCBldWlzbW9kLiBTdXNwZW5kaXNzZSBxdWlz +IG1hZ25hIG1hZ25hLCBhIHRpbmNpZHVudCBvcmNpLiBMb3JlbSBpcHN1bSBkb2xv +ciBzaXQgYW1ldCwgY29uc2VjdGV0dXIgYWRpcGlzY2luZyBlbGl0LiBEb25lYyBu +aXNpIHR1cnBpcywgYWxpcXVldCBldSBzb2xsaWNpdHVkaW4gdml0YWUsIGxhb3Jl +ZXQgbmVjIG5pc2kuIFZlc3RpYnVsdW0gdHJpc3RpcXVlLCB2ZWxpdCB2aXRhZSB1 +bGxhbWNvcnBlciBjb25kaW1lbnR1bSwgbGVjdHVzIG9yY2kgcG9ydHRpdG9yIGFy +Y3UsIGV1IHBsYWNlcmF0IGZlbGlzIHB1cnVzIG5lYyBvcmNpLiBOYW0gaXBzdW0g +YXVndWUsIHNjZWxlcmlzcXVlIGF0IGxhY2luaWEgc2l0IGFtZXQsIG1hdHRpcyBh +IGVyYXQuIFBoYXNlbGx1cyBhIG1hZ25hIHF1aXMgbGFjdXMgdmFyaXVzIHBsYWNl +cmF0LgoKUHJvaW4gb3JuYXJlIHZpdmVycmEgcGxhY2VyYXQuIFNlZCBpYWN1bGlz +IHVsdHJpY2VzIG1hZ25hLCBjb252YWxsaXMgYXVjdG9yIG1pIHRyaXN0aXF1ZSB2 +ZWwuIFBlbGxlbnRlc3F1ZSBhYyBuaXNpIHNpdCBhbWV0IG5pc2kgYWxpcXVhbSB0 +aW5jaWR1bnQgYWMgdXQgaXBzdW0uIFByYWVzZW50IGEgdGVsbHVzIG5vbiBudW5j +IGlhY3VsaXMgYWRpcGlzY2luZy4gUXVpc3F1ZSBmYWNpbGlzaXMganVzdG8gZWdl +dCBtZXR1cyBncmF2aWRhIHVsbGFtY29ycGVyLiBEb25lYyBzYWdpdHRpcywgdG9y +dG9yIGV1aXNtb2QgYWxpcXVhbSBpbnRlcmR1bSwgYXVndWUgYXVndWUgbGFvcmVl +dCBlbGl0LCBpbiBjb21tb2RvIHVybmEgbGFjdXMgdmVsIGVzdC4gVmVzdGlidWx1 +bSBkdWkgbmliaCwgdmFyaXVzIGEgaW50ZXJkdW0gYSwgcG9ydGEgdXQgbWV0dXMu +IFV0IGxlY3R1cyB1cm5hLCBwb3N1ZXJlIGluIGx1Y3R1cyBldCwgcnV0cnVtIGEg +ZXJhdC4gU3VzcGVuZGlzc2UgY3Vyc3VzLCB0b3J0b3Igdml0YWUgc2NlbGVyaXNx +dWUgdHJpc3RpcXVlLCBkb2xvciBlcmF0IGRpZ25pc3NpbSBsZWN0dXMsIG5lYyBz +b2RhbGVzIG1hdXJpcyB0ZWxsdXMgdmVsIHB1cnVzLiBEdWlzIGVsaXQgbWF1cmlz +LCBhY2N1bXNhbiB1dCBwb3N1ZXJlIHZ1bHB1dGF0ZSwgbWFsZXN1YWRhIGV0IGFu +dGUuIE1vcmJpIGJsYW5kaXQgbGFjdXMgYXQgbWF1cmlzIHNhZ2l0dGlzIHJ1dHJ1 +bS4gUGhhc2VsbHVzIGRvbG9yIG1hdXJpcywgY29uc2VxdWF0IHZlbCBmYXVjaWJ1 +cyB2ZWwsIHB1bHZpbmFyIG5lYyBhcmN1LiBOYW0gY29tbW9kbywgcHVydXMgdml0 +YWUgbW9sbGlzIHNjZWxlcmlzcXVlLCBzYXBpZW4gdHVycGlzIGJpYmVuZHVtIGFu +dGUsIGV1IGZhY2lsaXNpcyBpcHN1bSBpcHN1bSBuZWMgbmVxdWUuIENsYXNzIGFw +dGVudCB0YWNpdGkgc29jaW9zcXUgYWQgbGl0b3JhIHRvcnF1ZW50IHBlciBjb251 +YmlhIG5vc3RyYSwgcGVyIGluY2VwdG9zIGhpbWVuYWVvcy4gUGVsbGVudGVzcXVl +IGNvbnZhbGxpcyBkaWduaXNzaW0gbWFnbmEgc2l0IGFtZXQgdnVscHV0YXRlLiBT +ZWQgY29uZGltZW50dW0gdmVoaWN1bGEgbWF0dGlzLiBWaXZhbXVzIHRpbmNpZHVu +dCBmYWNpbGlzaXMgaXBzdW0sIGV0IHBvcnRhIHJpc3VzIHVsdHJpY2VzIHZpdGFl +LiBRdWlzcXVlIG5vbiBjb25zZXF1YXQgbmlzbC4gQ3VyYWJpdHVyIGp1c3RvIG51 +bGxhLCBiaWJlbmR1bSBpbiB2ZW5lbmF0aXMgZWdldCwgcG9zdWVyZSBlZ2V0IHNl +bS4KClBlbGxlbnRlc3F1ZSBzaXQgYW1ldCByaXN1cyB2ZWwgbGliZXJvIGV1aXNt +b2Qgc3VzY2lwaXQgdmVsIHNpdCBhbWV0IG1ldHVzLiBEb25lYyBncmF2aWRhLCBs +ZW8gcXVpcyBpbnRlcmR1bSBjb25ndWUsIHF1YW0gbGVjdHVzIHRyaXN0aXF1ZSBu +aXNpLCBldSB1bHRyaWNlcyBzZW0gdHVycGlzIHRpbmNpZHVudCBlc3QuIFByYWVz +ZW50IHB1bHZpbmFyIGNvbnZhbGxpcyB1cm5hIHZpdGFlIGlhY3VsaXMuIFV0IGNv +bnZhbGxpcyBwdWx2aW5hciBmYWNpbGlzaXMuIEN1bSBzb2NpaXMgbmF0b3F1ZSBw +ZW5hdGlidXMgZXQgbWFnbmlzIGRpcyBwYXJ0dXJpZW50IG1vbnRlcywgbmFzY2V0 +dXIgcmlkaWN1bHVzIG11cy4gQWVuZWFuIGV0IGVyYXQgZG9sb3IsIGFjIHBvc3Vl +cmUgbWV0dXMuIFNlZCBjb25kaW1lbnR1bSBhZGlwaXNjaW5nIGlwc3VtLCBlZ2V0 +IHVsdHJpY2llcyBqdXN0byBmZXJtZW50dW0gc2l0IGFtZXQuIE5hbSBmZXJtZW50 +dW0gbGVvIHZpdGFlIGxlY3R1cyBtYXR0aXMgaW1wZXJkaWV0LiBFdGlhbSBkb2xv +ciBsYWN1cywgbWF0dGlzIGF0IHRlbXB1cyBldSwgY29uZ3VlIHZlbCB2ZWxpdC4g +UHJhZXNlbnQgYmxhbmRpdCB2aXZlcnJhIHJob25jdXMuIFBlbGxlbnRlc3F1ZSB2 +aXRhZSBsYWN1cyBsZW8uCgpRdWlzcXVlIGlkIGxvcmVtIHF1aXMgdHVycGlzIGx1 +Y3R1cyBjb25zZXF1YXQgaW4gc2l0IGFtZXQgZXN0LiBDdXJhYml0dXIgdHJpc3Rp +cXVlLCBhcmN1IGEgY3Vyc3VzIHZvbHV0cGF0LCBudWxsYSBlc3QgYXVjdG9yIHRl +bGx1cywgbmVjIGZhY2lsaXNpcyBuaWJoIGF1Z3VlIG5lYyBvcmNpLiBNYXVyaXMg +ZmV1Z2lhdCwgZXN0IGFjIGF1Y3RvciBldWlzbW9kLCBvZGlvIG5pYmggYWNjdW1z +YW4gbmVxdWUsIGluIGZlcm1lbnR1bSBvcmNpIG1hc3NhIHZpdGFlIGF1Z3VlLiBQ +ZWxsZW50ZXNxdWUgcXVpcyBtaSBhcmN1LCBub24gaW1wZXJkaWV0IGlwc3VtLiBO +dWxsYW0gZGFwaWJ1cyBoZW5kcmVyaXQgZmVsaXMsIGFjIHVsbGFtY29ycGVyIHRv +cnRvciBwaGFyZXRyYSBldC4gTnVsbGEgYWNjdW1zYW4sIGxhY3VzIHNlZCBlbGVt +ZW50dW0gaW50ZXJkdW0sIG1pIGFyY3UgdmVzdGlidWx1bSBuZXF1ZSwgZWdldCBm +YWNpbGlzaXMgZXJvcyBudW5jIGF0IGVsaXQuIFN1c3BlbmRpc3NlIGV1IG1hdXJp +cyBzdXNjaXBpdCBwdXJ1cyBkaWN0dW0gaWFjdWxpcy4gUHJhZXNlbnQgc2l0IGFt +ZXQgbnVuYyBuZWMgbGFjdXMgZmFjaWxpc2lzIHBvcnRhLiBNYXVyaXMgbnVuYyBp +cHN1bSwgc29sbGljaXR1ZGluIHNlZCBwb3N1ZXJlIGluLCBmZXJtZW50dW0gYWMg +dGVsbHVzLiBOdWxsYSBmcmluZ2lsbGEgc2NlbGVyaXNxdWUgZXJhdCBpZCBwbGFj +ZXJhdC4KCkludGVnZXIgZnJpbmdpbGxhIGZlcm1lbnR1bSB0dXJwaXMgZWdldCBs +YW9yZWV0LiBTdXNwZW5kaXNzZSBwb3N1ZXJlIGVzdCBhYyBlcm9zIGNvbnNlcXVh +dCBub24gZGlnbmlzc2ltIHB1cnVzIGFsaXF1ZXQuIE1hdXJpcyBwcmV0aXVtIHZl +bmVuYXRpcyBwdWx2aW5hci4gVXQgbmVxdWUgbWV0dXMsIGN1cnN1cyBpZCBkaWdu +aXNzaW0gc2l0IGFtZXQsIGNvbmRpbWVudHVtIGVnZXQgZXJvcy4gQWxpcXVhbSBl +cmF0IHZvbHV0cGF0LiBFdGlhbSBwb3J0YSBhbGlxdWFtIG5pc2ksIHNpdCBhbWV0 +IGNvbnNlY3RldHVyIHB1cnVzIGxvYm9ydGlzIG5lYy4gVmVzdGlidWx1bSB0aW5j +aWR1bnQgcGxhY2VyYXQgcXVhbSBhYyBjdXJzdXMuIFByYWVzZW50IGxhb3JlZXQg +ZXJvcyBub24gbmVxdWUgcmhvbmN1cyBhYyBoZW5kcmVyaXQgZHVpIHRlbXBvci4g +Q3JhcyBzb2RhbGVzIGVsaXQgdml0YWUgdXJuYSBpbnRlcmR1bSBhY2N1bXNhbi4g +TnVsbGFtIG5lYyBsYW9yZWV0IGFyY3UuIERvbmVjIGZyaW5naWxsYSBzY2VsZXJp +c3F1ZSByaXN1cywgbmVjIHNhZ2l0dGlzIG9kaW8gYWNjdW1zYW4gbm9uLiBRdWlz +cXVlIHJ1dHJ1bSBzb2RhbGVzIG9kaW8uIE1hdXJpcyBmYWNpbGlzaXMsIG5pYmgg +cXVpcyBlbGVpZmVuZCBzb2xsaWNpdHVkaW4sIGR1aSBvZGlvIGF1Y3RvciBvcmNp +LCBldSBwb3J0dGl0b3IgYXJjdSBudW5jIGFjIGxpZ3VsYS4gSW4gc2VkIG1pIG5l +cXVlLiBNYXVyaXMgZWdlc3RhcywgdGVsbHVzIHNlZCBldWlzbW9kIGVnZXN0YXMs +IGVyb3MgbGlndWxhIGdyYXZpZGEgcmlzdXMsIGF0IGFsaXF1YW0gbGVvIGxpZ3Vs +YSB1dCBvZGlvLiBBbGlxdWFtIHZlbCB2YXJpdXMgc2FwaWVuLiBFdGlhbSBldSBs +ZW8gZXJvcywgcXVpcyBjb25zZXF1YXQgbWkuIE51bGxhbSBzb2RhbGVzIHBlbGxl +bnRlc3F1ZSBvZGlvIG5vbiB0cmlzdGlxdWUuCgpBbGlxdWFtIGVyYXQgdm9sdXRw +YXQuIEFlbmVhbiBsYW9yZWV0LCBudW5jIGVnZXQgbW9sbGlzIGF1Y3RvciwgbWkg +bGlndWxhIGxvYm9ydGlzIGR1aSwgYSB1bHRyaWNpZXMgcXVhbSB2ZWxpdCB1dCBz +YXBpZW4uIFV0IHZlbCBpYWN1bGlzIG5pYmguIEV0aWFtIHV0IHJpc3VzIGR1aS4g +TWF1cmlzIGF0IGp1c3RvIGZlbGlzLiBNYXVyaXMgaWFjdWxpcyBiaWJlbmR1bSB2 +ZWxpdCBlZ2V0IGRhcGlidXMuIEFsaXF1YW0gZXJhdCB2b2x1dHBhdC4gTnVsbGEg +c2l0IGFtZXQgb3JjaSBhbnRlLCBhIGVnZXN0YXMgc2FwaWVuLiBNb3JiaSB1bGxh +bWNvcnBlciBsZWN0dXMgdmVsIG1hdXJpcyB2ZXN0aWJ1bHVtIG1hbGVzdWFkYS4g +Q3JhcyB2ZWwgbGVjdHVzIGlwc3VtLiBEdWlzIGVnZXN0YXMgdmVuZW5hdGlzIHBy +ZXRpdW0uIE1vcmJpIHNlZCB0b3J0b3IgZXUgb2RpbyBzdXNjaXBpdCBydXRydW0u +IFNlZCB1bHRyaWNlcyBtYXNzYSBmZXJtZW50dW0gbGFjdXMgdGVtcHVzIHBoYXJl +dHJhLiBJbiBldCBtYXVyaXMgcXVhbSwgaWQgZnJpbmdpbGxhIG1ldHVzLgoKQWVu +ZWFuIG5lYyB2ZWxpdCBkdWkuIE51bGxhbSBlZ2VzdGFzIG1pIGV1IGlwc3VtIHVs +dHJpY2VzIGVnZXQgdmVuZW5hdGlzIHZlbGl0IGxhY2luaWEuIFZpdmFtdXMgbmVj +IGxpZ3VsYSBzaXQgYW1ldCBqdXN0byBhZGlwaXNjaW5nIHZhcml1cyB1dCB1dCBw +dXJ1cy4gQWVuZWFuIGx1Y3R1cyBuaXNsIGVnZXQgbmlzbCB1bHRyaWNlcyBpbXBl +cmRpZXQuIFZpdmFtdXMgYSBuaWJoIGF0IGVsaXQgc29kYWxlcyBldWlzbW9kLiBB +ZW5lYW4gaGVuZHJlcml0IG5pc2kgdmVsIG1ldHVzIGNvbnZhbGxpcyB2dWxwdXRh +dGUuIE1vcmJpIHVybmEgdHVycGlzLCB0ZW1wb3IgYXQgcHVsdmluYXIgdmVsLCBj +b25zZWN0ZXR1ciBldSBtZXR1cy4gTmFtIGZyaW5naWxsYSBtYXVyaXMgc2VkIG9y +Y2kgcG9ydGEgYWMgbW9sbGlzIG51bGxhIGJsYW5kaXQuIEluIHVsdHJpY2llcyB2 +ZWxpdCBhdWN0b3IgYXJjdSBncmF2aWRhIGZlcm1lbnR1bS4gVXQgdmVsIG5lcXVl +IGV0IHZlbGl0IHByZXRpdW0gYmxhbmRpdCBldSBpbiBhbnRlLiBWZXN0aWJ1bHVt +IHV0IGR1aSBtYWduYS4gTmFtIGV0IGxhY3VzIGlwc3VtLCBzZWQgdGluY2lkdW50 +IHRlbGx1cy4gRG9uZWMgZWxlbWVudHVtIGVsZWlmZW5kIHRvcnRvciB1dCB2ZXN0 +aWJ1bHVtLgoKRXRpYW0gdm9sdXRwYXQgbWV0dXMgc2VkIGxvcmVtIGRhcGlidXMg +bGFjaW5pYS4gQ2xhc3MgYXB0ZW50IHRhY2l0aSBzb2Npb3NxdSBhZCBsaXRvcmEg +dG9ycXVlbnQgcGVyIGNvbnViaWEgbm9zdHJhLCBwZXIgaW5jZXB0b3MgaGltZW5h +ZW9zLiBWZXN0aWJ1bHVtIGEgYmxhbmRpdCBuaWJoLiBQaGFzZWxsdXMgZXQgbG9y +ZW0gdmVsIGVyb3MgaWFjdWxpcyB1bHRyaWNpZXMuIFNlZCBzaXQgYW1ldCBtYWdu +YSBzaXQgYW1ldCBlc3QgbW9sZXN0aWUgcHJldGl1bSBzZWQgbmVjIG9yY2kuIFN1 +c3BlbmRpc3NlIHBvdGVudGkuIE51bGxhIGNvbnZhbGxpcyBhbnRlIHZpdGFlIGRv +bG9yIGNvbnZhbGxpcyBub24gdmFyaXVzIG5lcXVlIHBlbGxlbnRlc3F1ZS4gQ3Jh +cyBldWlzbW9kIG1hc3NhIGEgZXJvcyBhbGlxdWFtIHVsdHJpY2VzLiBTZWQgdml0 +YWUgcHVydXMgdXQgbmlzaSBmYXVjaWJ1cyBmZXVnaWF0LiBQcmFlc2VudCBhIGxl +Y3R1cyBldCB2ZWxpdCBlZ2VzdGFzIHBoYXJldHJhIGV0IHNpdCBhbWV0IGRvbG9y +LiBEdWlzIHF1aXMgbGFjaW5pYSBvZGlvLiBTZWQgc2VkIGVuaW0gbGVvLiBEb25l +YyBwdWx2aW5hciBzb2xsaWNpdHVkaW4gbmVxdWUgdXQgZnJpbmdpbGxhLiBFdGlh +bSBtYWxlc3VhZGEgbmlzbCBkaWN0dW0gZXJhdCBkaWN0dW0gZGFwaWJ1cy4gRnVz +Y2UgZmFjaWxpc2lzLCBtaSB2ZWwgdmFyaXVzIG9ybmFyZSwgdG9ydG9yIG51bGxh +IG9ybmFyZSBlcm9zLCBhdCBldWlzbW9kIG5pYmggbmlzaSBldCBhdWd1ZS4gTWFl +Y2VuYXMgZWdldCBzYXBpZW4gbWkuIFNlZCBzZWQgbnVsbGEgbGVjdHVzLiBOdW5j +IGRpZ25pc3NpbSBsdWN0dXMgbGVjdHVzLCBhdCBhbGlxdWV0IGRvbG9yIHZlbmVu +YXRpcyBxdWlzLiBVdCBwdWx2aW5hciwgdG9ydG9yIHNpdCBhbWV0IHNjZWxlcmlz +cXVlIHBoYXJldHJhLCBmZWxpcyBsZW8gcHJldGl1bSBmZWxpcywgZXQgbWF0dGlz +IHNhcGllbiBtYXVyaXMgbWF0dGlzIG51bGxhLgoKU3VzcGVuZGlzc2UgcG90ZW50 +aS4gTnVuYyBxdWlzIHB1bHZpbmFyIHF1YW0uIER1aXMgZGFwaWJ1cyBiaWJlbmR1 +bSBmYWNpbGlzaXMuIE51bGxhbSBsb2JvcnRpcyBlcmF0IHNpdCBhbWV0IHF1YW0g +YWRpcGlzY2luZyBldSBtb2xlc3RpZSB0b3J0b3IgYWNjdW1zYW4uIFByYWVzZW50 +IHB1bHZpbmFyIGVuaW0gZXUganVzdG8gZGFwaWJ1cyBpbiB2aXZlcnJhIG1hdXJp +cyBjb21tb2RvLiBFdGlhbSBtb2xsaXMgY29uc2VxdWF0IHZlbGl0LCBub24gbW9s +ZXN0aWUgZXJvcyBjb25kaW1lbnR1bSBhYy4gRXRpYW0gdml0YWUgZXJhdCBuZWMg +ZWxpdCBjb25ndWUgY29uZGltZW50dW0gYSB1bGxhbWNvcnBlciB0dXJwaXMuIFZl +c3RpYnVsdW0gYW50ZSBpcHN1bSBwcmltaXMgaW4gZmF1Y2lidXMgb3JjaSBsdWN0 +dXMgZXQgdWx0cmljZXMgcG9zdWVyZSBjdWJpbGlhIEN1cmFlOyBWZXN0aWJ1bHVt +IHZpdGFlIGxvcmVtIGV0IGxlY3R1cyBjb252YWxsaXMgY29uc2VjdGV0dXIuIFN1 +c3BlbmRpc3NlIHNpdCBhbWV0IGZlbGlzIG5vbiBtZXR1cyBtYXR0aXMgbG9ib3J0 +aXMuIE1vcmJpIGV1IG51bGxhIHRvcnRvci4gTWFlY2VuYXMgaGVuZHJlcml0IG9y +Y2kgc2l0IGFtZXQgbGlndWxhIGludGVyZHVtIGV0IGlhY3VsaXMgbnVsbGEgcGxh +Y2VyYXQuIE5hbSBkaWN0dW0gbGFjdXMgYXQgYXJjdSByaG9uY3VzIGVsZWlmZW5k +LiBJbiBoYWMgaGFiaXRhc3NlIHBsYXRlYSBkaWN0dW1zdC4KCkN1cmFiaXR1ciBh +YyB2ZW5lbmF0aXMgZHVpLiBDbGFzcyBhcHRlbnQgdGFjaXRpIHNvY2lvc3F1IGFk +IGxpdG9yYSB0b3JxdWVudCBwZXIgY29udWJpYSBub3N0cmEsIHBlciBpbmNlcHRv +cyBoaW1lbmFlb3MuIEludGVnZXIgYXVjdG9yIHBvcnRhIG5lcXVlIHZlbCBwZWxs +ZW50ZXNxdWUuIEFlbmVhbiBhYyBwdXJ1cyBxdWlzIHZlbGl0IGlhY3VsaXMgcG9y +dHRpdG9yIGluIGluIHNlbS4gRHVpcyB0aW5jaWR1bnQgcmlzdXMgaW4gcmlzdXMg +ZmF1Y2lidXMgYSBhbGlxdWFtIGRpYW0gYXVjdG9yLiBOdWxsYW0gc2VtIGF1Z3Vl +LCBhZGlwaXNjaW5nIHNlZCB0aW5jaWR1bnQgcXVpcywgbW9sZXN0aWUgc2VkIGxp +YmVyby4gVmVzdGlidWx1bSBjb21tb2RvIG9kaW8gdGVtcG9yIG51bGxhIG9ybmFy +ZSBwbGFjZXJhdC4gTW9yYmkgZG9sb3IgbWFzc2EsIGJpYmVuZHVtIGluIGVsZWlm +ZW5kIGlkLCBtb2xlc3RpZSB1dCBqdXN0by4gUXVpc3F1ZSB2YXJpdXMgbnVuYyBz +aXQgYW1ldCBuaXNsIGRhcGlidXMgZmFjaWxpc2lzLiBEb25lYyB1dCBlcmF0IG1p +LiBEdWlzIGVnZXQgY29uc2VjdGV0dXIgbWFnbmEuIE1hdXJpcyBlZ2VzdGFzIHNl +bXBlciBlZ2VzdGFzLiBTZWQgZXQganVzdG8gc2VkIG51bGxhIGJsYW5kaXQgYWNj +dW1zYW4gYXQgbm9uIGxlby4gSW4gZWdldCBlc3QgaXBzdW0uIE51bGxhIHRlbGx1 +cyBsaWd1bGEsIGFsaXF1ZXQgc2l0IGFtZXQgdnVscHV0YXRlIHZpdGFlLCBtb2xs +aXMgZXUgZXJhdC4KCkFlbmVhbiBhYyBhdWd1ZSBvZGlvLiBQcm9pbiBtb2xsaXMs +IGRvbG9yIHV0IGZldWdpYXQgc3VzY2lwaXQsIGR1aSBhbnRlIGNvbnNlY3RldHVy +IG9kaW8sIHZpdGFlIGNvbmRpbWVudHVtIHRlbGx1cyBudWxsYSBhYyBmZWxpcy4g +UGVsbGVudGVzcXVlIGVnZXN0YXMgdWx0cmljZXMgbnVuYywgZXUgc2FnaXR0aXMg +c2FwaWVuIGV1aXNtb2Qgdml0YWUuIEludGVnZXIgc29sbGljaXR1ZGluIGZldWdp +YXQgbGVvIG5lYyBjb25zZWN0ZXR1ci4gUHJhZXNlbnQgc2l0IGFtZXQgc2VtIGVy +YXQsIG5lYyB2b2x1dHBhdCBsYWN1cy4gRXRpYW0gYSBsYWN1cyBudWxsYSwgbm9u +IGludGVyZHVtIGVyYXQuIE1hZWNlbmFzIHF1aXMgZGljdHVtIGxlY3R1cy4gUGVs +bGVudGVzcXVlIGFjIGxpYmVybyB2ZWwgZWxpdCB0cmlzdGlxdWUgc2VtcGVyLiBR +dWlzcXVlIGV1IHN1c2NpcGl0IGR1aS4gSW4gaGFjIGhhYml0YXNzZSBwbGF0ZWEg +ZGljdHVtc3QuIENyYXMgZXUgcG9ydGEgcXVhbS4gQ3VyYWJpdHVyIHBoYXJldHJh +LCBmZWxpcyB1dCByaG9uY3VzIHRpbmNpZHVudCwgbnVsbGEgYXVndWUgcGxhY2Vy +YXQgc2FwaWVuLCBpZCB2ZXN0aWJ1bHVtIG5pc2wgbmVxdWUgYXQgZGlhbS4KClF1 +aXNxdWUgbGFvcmVldCBkYXBpYnVzIGx1Y3R1cy4gTW9yYmkgZXJhdCBvZGlvLCBt +YXR0aXMgc2l0IGFtZXQgdWx0cmljaWVzIGV1LCBpbnRlcmR1bSBldCBudWxsYS4g +TmFtIHZ1bHB1dGF0ZSwgbWV0dXMgbm9uIGVsZW1lbnR1bSBoZW5kcmVyaXQsIHVy +bmEgcHVydXMgcGhhcmV0cmEgZWxpdCwgdml0YWUgc29kYWxlcyBwdXJ1cyBsaWd1 +bGEgbm9uIHF1YW0uIEV0aWFtIGlkIGxpZ3VsYSB0aW5jaWR1bnQgbGVvIHBlbGxl +bnRlc3F1ZSBibGFuZGl0LiBVdCBhbnRlIHVybmEsIHZlc3RpYnVsdW0gc2VkIGVs +ZW1lbnR1bSBhYywgcGVsbGVudGVzcXVlIHNpdCBhbWV0IHRvcnRvci4gRnVzY2Ug +dmVuZW5hdGlzIGNvbW1vZG8gYW50ZSBhdCB0ZW1wb3IuIE51bGxhIGluIG9kaW8g +bGVjdHVzLiBEb25lYyBsYWNpbmlhIGRhcGlidXMgdmVoaWN1bGEuIFN1c3BlbmRp +c3NlIHNjZWxlcmlzcXVlIG9kaW8gdXQgbG9yZW0gZGlnbmlzc2ltIGlkIHByZXRp +dW0gdG9ydG9yIHN1c2NpcGl0LiBOdWxsYW0gZmVsaXMgYW50ZSwgZmVybWVudHVt +IGVnZXQgc29sbGljaXR1ZGluIHNpdCBhbWV0LCBjb21tb2RvIGF0IGVuaW0uIERv +bmVjIGFsaXF1ZXQgbWF1cmlzIGluIGVsaXQgYXVjdG9yIGF0IGNvbnZhbGxpcyBx +dWFtIHVsbGFtY29ycGVyLiBEdWlzIGVuaW0gbmliaCwgdnVscHV0YXRlIGluIGZh +Y2lsaXNpcyBpZCwgYWRpcGlzY2luZyBpbnRlcmR1bSBsYWN1cy4gQWVuZWFuIHNv +bGxpY2l0dWRpbiBjb25ndWUgY29uc2VjdGV0dXIuIFByYWVzZW50IGxvYm9ydGlz +IG5pc2wgZXQgbWV0dXMgZXVpc21vZCBjb25ndWUuIFN1c3BlbmRpc3NlIGlkIHRl +bXB1cyBpcHN1bS4KCkRvbmVjIGV1IGF1Y3RvciBzYXBpZW4uIEluIHBsYWNlcmF0 +IGF1Y3RvciBtYXNzYSB1dCBwbGFjZXJhdC4gVml2YW11cyBhdCB0dXJwaXMgZWxp +dC4gRG9uZWMgY29uZ3VlIHJob25jdXMgZXN0IGEgdml2ZXJyYS4gTnVuYyBub24g +b2RpbyBlbmltLiBDdW0gc29jaWlzIG5hdG9xdWUgcGVuYXRpYnVzIGV0IG1hZ25p +cyBkaXMgcGFydHVyaWVudCBtb250ZXMsIG5hc2NldHVyIHJpZGljdWx1cyBtdXMu +IE1hZWNlbmFzIHF1aXMgbWFzc2EgbGliZXJvLCBxdWlzIGVsZW1lbnR1bSBtYXVy +aXMuIFZlc3RpYnVsdW0gYW50ZSBpcHN1bSBwcmltaXMgaW4gZmF1Y2lidXMgb3Jj +aSBsdWN0dXMgZXQgdWx0cmljZXMgcG9zdWVyZSBjdWJpbGlhIEN1cmFlOyBWZXN0 +aWJ1bHVtIHZhcml1cyB2ZWxpdCBhIG9yY2kgZ3JhdmlkYSB1bHRyaWNpZXMgc2Vk +IHZlbCBsaWd1bGEuIFZlc3RpYnVsdW0gcHJldGl1bSB2ZWhpY3VsYSBhbGlxdWV0 +LiBTdXNwZW5kaXNzZSBhdWN0b3IgY29uZ3VlIG1hZ25hLCBhYyBjb252YWxsaXMg +ZGlhbSB1bGxhbWNvcnBlciB2ZWwuCgpBZW5lYW4gcGxhY2VyYXQgbW9sbGlzIGlw +c3VtLCBuZWMgdWxsYW1jb3JwZXIgcXVhbSBoZW5kcmVyaXQgZWdldC4gQWVuZWFu +IHNlZCBpcHN1bSBhIGFyY3UgbG9ib3J0aXMgdGluY2lkdW50LiBDbGFzcyBhcHRl +bnQgdGFjaXRpIHNvY2lvc3F1IGFkIGxpdG9yYSB0b3JxdWVudCBwZXIgY29udWJp +YSBub3N0cmEsIHBlciBpbmNlcHRvcyBoaW1lbmFlb3MuIEV0aWFtIGNvbnZhbGxp +cyB0b3J0b3Igc2FnaXR0aXMgbmlzbCBwb3J0YSBmZXJtZW50dW0uIFZpdmFtdXMg +YWNjdW1zYW4gbHVjdHVzIGNvbmd1ZS4gTWF1cmlzIGV0IGxlY3R1cyBsb3JlbS4g +TnVuYyBldCBudW5jIGV0IGF1Z3VlIGdyYXZpZGEgYmliZW5kdW0uIE51bGxhbSBz +dXNjaXBpdCBhcmN1IGV0IG1hdXJpcyBpYWN1bGlzIHZpdGFlIGNvbmd1ZSBuaXNs +IHRlbXB1cy4gQ2xhc3MgYXB0ZW50IHRhY2l0aSBzb2Npb3NxdSBhZCBsaXRvcmEg +dG9ycXVlbnQgcGVyIGNvbnViaWEgbm9zdHJhLCBwZXIgaW5jZXB0b3MgaGltZW5h +ZW9zLiBDcmFzIGRvbG9yIHRlbGx1cywgbHVjdHVzIHNlZCBpbXBlcmRpZXQgZXUs +IGFjY3Vtc2FuIG5lYyBsb3JlbS4gQ3JhcyBxdWlzIGlwc3VtIGFudGUuIFZlc3Rp +YnVsdW0gYW50ZSBpcHN1bSBwcmltaXMgaW4gZmF1Y2lidXMgb3JjaSBsdWN0dXMg +ZXQgdWx0cmljZXMgcG9zdWVyZSBjdWJpbGlhIEN1cmFlOyBRdWlzcXVlIHZpdGFl +IGRpYW0gYXVndWUsIGV1IHVsdHJpY2VzIGFyY3UuIFByYWVzZW50IGFudGUgdmVs +aXQsIHZlaGljdWxhIGVnZXQgY3Vyc3VzIGV0LCBjb25zZXF1YXQgc2l0IGFtZXQg +dG9ydG9yLiBNYWVjZW5hcyB1bHRyaWNlcyBsaWd1bGEgaW4gb2RpbyB2ZWhpY3Vs +YSB0aW5jaWR1bnQuIEFlbmVhbiB1bHRyaWNpZXMgaXBzdW0gdXQgc2VtIHZ1bHB1 +dGF0ZSB2aXRhZSBwdWx2aW5hciBkaWFtIGNvbnNlY3RldHVyLiBOYW0gdmVsIGVn +ZXN0YXMgZXJvcy4KClByb2luIHVybmEgbmliaCwgYWxpcXVldCBuZWMgc2FnaXR0 +aXMgYSwgY29uZ3VlIHNlZCBqdXN0by4gTWF1cmlzIHJpc3VzIG5lcXVlLCBibGFu +ZGl0IGN1cnN1cyBzZW1wZXIgZXUsIGZlcm1lbnR1bSBlZ2V0IHB1cnVzLiBDdXJh +Yml0dXIgaW4gbGFjdXMgYXVndWUsIHNpdCBhbWV0IGNvbmRpbWVudHVtIG1pLiBN +YXVyaXMgZXUgc2VtIGlwc3VtLCBpbiB1bHRyaWNlcyBtZXR1cy4gUXVpc3F1ZSBm +cmluZ2lsbGEgc2VtIGEgbmlzaSBjb21tb2RvIHZhcml1cy4gTnVuYyB1bHRyaWNl +cyBwbGFjZXJhdCBwbGFjZXJhdC4gVml2YW11cyBub24gbGVjdHVzIGRvbG9yLCBl +Z2V0IGhlbmRyZXJpdCBhdWd1ZS4gTmFtIHV0IG1hdHRpcyBwdXJ1cy4gSW50ZWdl +ciB2ZWwgdXJuYSBldCB0ZWxsdXMgbGFjaW5pYSBmYWNpbGlzaXMgcGVsbGVudGVz +cXVlIHV0IHRlbGx1cy4gRXRpYW0gbGFvcmVldCByaXN1cyBxdWlzIGVsaXQgZGlj +dHVtIGlkIHRyaXN0aXF1ZSBsZW8gc2VtcGVyLiBVdCBvcm5hcmUgbmlzbCBldSBu +aXNsIHVsbGFtY29ycGVyIGluIHNvbGxpY2l0dWRpbiBtYXVyaXMgZ3JhdmlkYS4g +RXRpYW0gaW4gdGluY2lkdW50IHZlbGl0LiBVdCB0ZW1wdXMgdHVycGlzIHZpdGFl +IHVybmEgc2FnaXR0aXMgdmVsIHBvcnRhIHNlbSB2b2x1dHBhdC4gRHVpcyBub24g +anVzdG8gbWV0dXMsIHRlbXBvciBmYXVjaWJ1cyBxdWFtLiBBbGlxdWFtIGluIGxh +Y3VzIG5lYyBtaSB2ZXN0aWJ1bHVtIGZyaW5naWxsYS4gUHJhZXNlbnQgdGVtcG9y +IGxlY3R1cyBhdCBtZXR1cyBwb3J0YSB1dCBjb25ndWUgZHVpIGZyaW5naWxsYS4K +Ck5hbSBhYyBsZWN0dXMgc2VtLCBhdCB2aXZlcnJhIHJpc3VzLiBDcmFzIHNpdCBh +bWV0IHNvZGFsZXMgbWFzc2EuIFF1aXNxdWUgY29uc2VjdGV0dXIgbGlndWxhIHBv +c3VlcmUgdHVycGlzIGV1aXNtb2QgaW50ZXJkdW0uIFV0IGV1IHRlbGx1cyBldSBt +YXNzYSB1bHRyaWNpZXMgYmliZW5kdW0gdml0YWUgaWQgbWV0dXMuIEN1cmFiaXR1 +ciBpbXBlcmRpZXQgY29uc2VxdWF0IHRpbmNpZHVudC4gVXQgZW5pbSBxdWFtLCBj +b25zZWN0ZXR1ciBhdCBwb3J0dGl0b3IgaW4sIHZhcml1cyBldSBtYWduYS4gTnVu +YyBhcmN1IGVsaXQsIHNvZGFsZXMgbm9uIGRhcGlidXMgdnVscHV0YXRlLCBsYWNp +bmlhIGlkIGZlbGlzLiBDdXJhYml0dXIgbHVjdHVzLCByaXN1cyBxdWlzIHZpdmVy +cmEgY29udmFsbGlzLCBtYXNzYSBqdXN0byB0ZW1wdXMgbGliZXJvLCB2aXRhZSBw +b3J0dGl0b3IgbGlndWxhIHZlbGl0IGEgdHVycGlzLiBOdW5jIHZpdmVycmEsIG1l +dHVzIGEgbWFsZXN1YWRhIGNvbnZhbGxpcywgbmVxdWUgYW50ZSBiaWJlbmR1bSBu +aXNsLCBlZ2V0IGltcGVyZGlldCBlbmltIGxlY3R1cyBldSBlbmltLiBQZWxsZW50 +ZXNxdWUgZW5pbSBuaXNsLCBhZGlwaXNjaW5nIGVnZXQgbW9sZXN0aWUgYXQsIGNv +bnNlY3RldHVyIGV1IG9yY2kuIFNlZCBwb3J0YSB1bGxhbWNvcnBlciBlc3QsIG5l +YyBkaWN0dW0gdHVycGlzIHNlbXBlciBzaXQgYW1ldC4KClF1aXNxdWUgcG9ydGEg +bWkgYWMgdmVsaXQgcmhvbmN1cyBhIHZlaGljdWxhIGxlbyBjb25zZXF1YXQuIE1v +cmJpIGxhb3JlZXQgbWF1cmlzIGVsaXQuIEZ1c2NlIHNlbXBlciByaXN1cyB2ZWwg +bGFjdXMgZWdlc3RhcyByaG9uY3VzLiBQZWxsZW50ZXNxdWUgdGluY2lkdW50IG51 +bGxhIGp1c3RvLCB2aXRhZSB2ZWhpY3VsYSBsaWJlcm8uIE1hdXJpcyBkYXBpYnVz +IGR1aSBpbiBtYWduYSB0cmlzdGlxdWUgc2l0IGFtZXQgZmFjaWxpc2lzIHJpc3Vz +IGxvYm9ydGlzLiBBZW5lYW4gc2l0IGFtZXQgZG9sb3Igdml0YWUgbG9yZW0gZXVp +c21vZCBjb252YWxsaXMuIE51bGxhbSBzaXQgYW1ldCBhZGlwaXNjaW5nIGp1c3Rv +LiBWaXZhbXVzIHZlc3RpYnVsdW0gb3JuYXJlIHN1c2NpcGl0LiBWZXN0aWJ1bHVt +IG1hbGVzdWFkYSB1bHRyaWNlcyB2ZWxpdCBub24gZmV1Z2lhdC4gVmVzdGlidWx1 +bSBhbnRlIGlwc3VtIHByaW1pcyBpbiBmYXVjaWJ1cyBvcmNpIGx1Y3R1cyBldCB1 +bHRyaWNlcyBwb3N1ZXJlIGN1YmlsaWEgQ3VyYWU7IE1hdXJpcyBhcmN1IGlwc3Vt +LCBjb21tb2RvIGluIGlhY3VsaXMgc2VkLCBjb25zZXF1YXQgdXQgYXVndWUuIEFs +aXF1YW0gbGliZXJvIG1hdXJpcywgYWxpcXVldCBhdCBjb25kaW1lbnR1bSBub24s +IGhlbmRyZXJpdCBuZWMgZXJhdC4gSW4gdXQgbWF1cmlzIGxvcmVtLiBWaXZhbXVz +IGVnZXQgbGlndWxhIGlkIG5pc2kgc29sbGljaXR1ZGluIHBsYWNlcmF0IGlkIG5l +YyB0ZWxsdXMuIE51bGxhIG5vbiBmYXVjaWJ1cyBsb3JlbS4gTnVsbGEgbm9uIG5p +YmggZXUgZW5pbSB2aXZlcnJhIGNvbnNlY3RldHVyLiBNb3JiaSBsaWJlcm8gbmlz +bCwgY29udmFsbGlzIGlkIGFsaXF1YW0gaWQsIHN1c2NpcGl0IGFjIGRpYW0uIENs +YXNzIGFwdGVudCB0YWNpdGkgc29jaW9zcXUgYWQgbGl0b3JhIHRvcnF1ZW50IHBl +ciBjb251YmlhIG5vc3RyYSwgcGVyIGluY2VwdG9zIGhpbWVuYWVvcy4gSW50ZWdl +ciBlZ2V0IGFudGUgc2FwaWVuLiBTZWQgbm9uIGF1Y3RvciBtYXNzYS4KCk1hZWNl +bmFzIHRpbmNpZHVudCBncmF2aWRhIGNvbnZhbGxpcy4gVmVzdGlidWx1bSBldCBp +YWN1bGlzIGxpYmVyby4gU2VkIHVybmEgb3JjaSwgY29udmFsbGlzIGluIGNvbnNl +cXVhdCBpZCwgdWx0cmljaWVzIHF1aXMgc2VtLiBFdGlhbSBhbGlxdWV0IHNhZ2l0 +dGlzIGltcGVyZGlldC4gTWF1cmlzIG5lYyBuZXF1ZSBxdWFtLiBNb3JiaSB0aW5j +aWR1bnQgbW9sbGlzIHZlbGl0IGVnZXQgdWx0cmljZXMuIERvbmVjIGF0IHR1cnBp +cyBtYWduYSwgZXUgcGxhY2VyYXQgZXJvcy4gUXVpc3F1ZSB0b3J0b3IgbmlzbCwg +dGluY2lkdW50IGV1IGltcGVyZGlldCBxdWlzLCBwb3N1ZXJlIG5lYyBlbGl0LiBD +dW0gc29jaWlzIG5hdG9xdWUgcGVuYXRpYnVzIGV0IG1hZ25pcyBkaXMgcGFydHVy +aWVudCBtb250ZXMsIG5hc2NldHVyIHJpZGljdWx1cyBtdXMuIFF1aXNxdWUgYWxp +cXVldCBsYW9yZWV0IGRpY3R1bS4KClF1aXNxdWUgdmVsIGJpYmVuZHVtIGxlby4g +RXRpYW0gZXQgbWF1cmlzIGxhY3VzLCBzZWQgc29sbGljaXR1ZGluIG5pc2kuIFF1 +aXNxdWUgaWQgcmlzdXMgaW4gbG9yZW0gbWF0dGlzIGRpY3R1bS4gU3VzcGVuZGlz +c2UgcG90ZW50aS4gRG9uZWMgbmVjIGp1c3RvIGFyY3UsIHV0IHBlbGxlbnRlc3F1 +ZSBzZW0uIFN1c3BlbmRpc3NlIHBvdGVudGkuIFBlbGxlbnRlc3F1ZSBlZ2V0IG5p +c2kgaWQgbmlzbCBsYWNpbmlhIGltcGVyZGlldC4gTmFtIGNvbnNlY3RldHVyIGZh +Y2lsaXNpcyBsZW8gbmVjIHRlbXBvci4gRHVpcyBmZXJtZW50dW0gbGFvcmVldCB0 +dXJwaXMgZXQgcHJldGl1bS4gQ2xhc3MgYXB0ZW50IHRhY2l0aSBzb2Npb3NxdSBh +ZCBsaXRvcmEgdG9ycXVlbnQgcGVyIGNvbnViaWEgbm9zdHJhLCBwZXIgaW5jZXB0 +b3MgaGltZW5hZW9zLiBBZW5lYW4gbGFvcmVldCBlc3Qgc2l0IGFtZXQgZWxpdCBm +ZXVnaWF0IGRhcGlidXMuIEV0aWFtIHRlbXB1cyBzdXNjaXBpdCB2ZWxpdCwgb3Ju +YXJlIGRhcGlidXMgaXBzdW0gcG9ydGEgbm9uLiBOdW5jIHV0IGRvbG9yIHV0IGR1 +aSBwb3J0YSBhY2N1bXNhbi4gSW50ZWdlciBkaWN0dW0sIHNlbSBub24gc3VzY2lw +aXQgc29sbGljaXR1ZGluLCB0b3J0b3IgZXJhdCBsYW9yZWV0IHRlbGx1cywgc2l0 +IGFtZXQgcHJldGl1bSBkb2xvciBtaSB1dCBpcHN1bS4gVml2YW11cyBhdWN0b3Ig +ZGlhbSBkaWN0dW0gc2FwaWVuIGZlcm1lbnR1bSBhZGlwaXNjaW5nLiBQaGFzZWxs +dXMgc2NlbGVyaXNxdWUgb2RpbyBxdWlzIG9yY2kgY3Vyc3VzIGV0IHRlbXBvciBz +ZW0gdGluY2lkdW50LiBJbiBoYWMgaGFiaXRhc3NlIHBsYXRlYSBkaWN0dW1zdC4g +QWVuZWFuIHNvZGFsZXMsIGVzdCBldCBtYXR0aXMgc29kYWxlcywgbG9yZW0gZGlh +bSBkYXBpYnVzIHF1YW0sIGFjIHByZXRpdW0gbGFjdXMgbGVjdHVzIGV1IHNhcGll +bi4gRG9uZWMgYWRpcGlzY2luZyBsb2JvcnRpcyBtaSBlZ2V0IHZlc3RpYnVsdW0u +CgpNYWVjZW5hcyBhdCBhbnRlIGVsaXQuIEluIHRlbXB1cyBydXRydW0gZXN0LCB2 +aXRhZSBlbGVtZW50dW0gbnVuYyBtb2xlc3RpZSBhYy4gQWxpcXVhbSB1dCBlbmlt +IGFyY3UsIG5lYyBzb2RhbGVzIG51bGxhLiBVdCBkaWduaXNzaW0gYWRpcGlzY2lu +ZyBvcm5hcmUuIER1aXMgZGljdHVtIGNvbW1vZG8gaXBzdW0sIGEgb3JuYXJlIHJp +c3VzIG1hbGVzdWFkYSBzZWQuIEV0aWFtIGlkIGFyY3UgbWF1cmlzLCBuZWMgcGhh +cmV0cmEgc2FwaWVuLiBMb3JlbSBpcHN1bSBkb2xvciBzaXQgYW1ldCwgY29uc2Vj +dGV0dXIgYWRpcGlzY2luZyBlbGl0LiBBZW5lYW4gcnV0cnVtIGxlY3R1cyBzZWQg +dHVycGlzIHBvcnR0aXRvciBoZW5kcmVyaXQuIEludGVnZXIgbWV0dXMgbGVvLCBs +dWN0dXMgYWMgbGFjaW5pYSBpbiwgZWxlaWZlbmQgZXQgbmVxdWUuIFBlbGxlbnRl +c3F1ZSBiaWJlbmR1bSBudWxsYSBzaXQgYW1ldCB0ZWxsdXMgZXVpc21vZCB2ZWwg +c2NlbGVyaXNxdWUgbWFnbmEgcnV0cnVtLiBBZW5lYW4gdWx0cmljZXMgY29udmFs +bGlzIG5pc2wsIGlkIG9ybmFyZSB0ZWxsdXMgYXVjdG9yIGV1LiBRdWlzcXVlIGxv +Ym9ydGlzIGF1Y3RvciBqdXN0bywgc2l0IGFtZXQgdGluY2lkdW50IGR1aSBsYWNp +bmlhIG5lYy4gTnVsbGFtIGNvbmd1ZSBudW5jIGF0IGZlbGlzIHBvcnR0aXRvciBy +dXRydW0gbm9uIGF0IGVuaW0uCgpVdCBjb252YWxsaXMgY29uZGltZW50dW0gbGFj +dXMgc2VkIGZhY2lsaXNpcy4gQWxpcXVhbSBwcmV0aXVtIGxpZ3VsYSBlZ2V0IG1h +Z25hIHBsYWNlcmF0IHZvbHV0cGF0LiBEb25lYyBub24gbG9yZW0gcGVsbGVudGVz +cXVlIGlwc3VtIGxvYm9ydGlzIHVsbGFtY29ycGVyLiBTdXNwZW5kaXNzZSB2b2x1 +dHBhdCBmZXJtZW50dW0gbWFsZXN1YWRhLiBQcmFlc2VudCBtb2xlc3RpZSBtYWxl +c3VhZGEgbG9yZW0sIHNpdCBhbWV0IGVsZWlmZW5kIG1hc3NhIGVsZWlmZW5kIG5l +Yy4gTnVsbGEgZmFjaWxpc2lzIHRpbmNpZHVudCBlcmF0LCBpbiBwb3J0dGl0b3Ig +ZXJvcyBzYWdpdHRpcyBpZC4gU2VkIGxvYm9ydGlzLCBsaWJlcm8gYXVjdG9yIGlu +dGVyZHVtIGhlbmRyZXJpdCwgZmVsaXMgcXVhbSB2ZXN0aWJ1bHVtIG5pYmgsIGVn +ZXQgaW1wZXJkaWV0IG1hZ25hIGxpZ3VsYSBhdCBudWxsYS4gQWxpcXVhbSBlcmF0 +IHZvbHV0cGF0LiBBbGlxdWFtIG5lYyBuaXNsIHZlbGl0LiBOdW5jIGRhcGlidXMg +ZGlnbmlzc2ltIG1hc3NhIGFjIGx1Y3R1cy4gTWF1cmlzIG5vbiBvZGlvIHB1cnVz +LiBEdWlzIGVyYXQgYW50ZSwgY29uc2VjdGV0dXIgcXVpcyBjb21tb2RvIGEsIGlh +Y3VsaXMgc2VkIGVzdC4gQ3VyYWJpdHVyIGp1c3RvIGp1c3RvLCBiaWJlbmR1bSBp +biBzZW1wZXIgdmVsLCBmYXVjaWJ1cyBpbiBuaWJoLiBFdGlhbSBtYWduYSBhcmN1 +LCBldWlzbW9kIGF0IHRpbmNpZHVudCBxdWlzLCBzb2xsaWNpdHVkaW4gZXQgbGVj +dHVzLiBOdWxsYW0gcHVydXMgbWV0dXMsIGZlcm1lbnR1bSBlZ2V0IHVsdHJpY2ll +cyBuZWMsIGNvbmd1ZSBhIG5pYmguIFBoYXNlbGx1cyBhIGxhY3VzIGlwc3VtLCBp +ZCBzb2xsaWNpdHVkaW4gdmVsaXQuCgpQaGFzZWxsdXMgbm9uIHZlbGl0IG9yY2ks +IGFjIGZldWdpYXQgbGFjdXMuIEN1cmFiaXR1ciBzZWQgc2FwaWVuIG1hZ25hLiBJ +biB2ZWwgcHVsdmluYXIgZmVsaXMuIE1hdXJpcyBxdWlzIGRpYW0gcXVpcyBtYXNz +YSB2YXJpdXMgdmFyaXVzLiBQcmFlc2VudCB1dCBhdWd1ZSBsZW8sIGV0IGZhY2ls +aXNpcyBtYXNzYS4gSW50ZWdlciBhdCB2ZWxpdCBvcmNpLCBuZWMgbW9sZXN0aWUg +ZXN0LiBNb3JiaSBwb3J0YSBibGFuZGl0IHR1cnBpcyBhdCBmcmluZ2lsbGEuIFNl +ZCBsYW9yZWV0IG9kaW8gdXQgZWxpdCBmZXJtZW50dW0gc2VkIGFjY3Vtc2FuIHRv +cnRvciB2b2x1dHBhdC4gVmVzdGlidWx1bSBhbnRlIGlwc3VtIHByaW1pcyBpbiBm +YXVjaWJ1cyBvcmNpIGx1Y3R1cyBldCB1bHRyaWNlcyBwb3N1ZXJlIGN1YmlsaWEg +Q3VyYWU7IEV0aWFtIGlkIG1hc3NhIHZpdGFlIGVyb3MgaWFjdWxpcyBmYWNpbGlz +aXMgc2VkIGFjIHNlbS4gRXRpYW0gdG9ydG9yIHF1YW0sIG1hbGVzdWFkYSBpbiBz +b2RhbGVzIGEsIHZ1bHB1dGF0ZSB0aW5jaWR1bnQgcXVhbS4gTnVsbGEgZmFjaWxp +c2kuIERvbmVjIGNvbmRpbWVudHVtIHZhcml1cyB1bHRyaWNlcy4gRnVzY2UgYmxh +bmRpdCwgdmVsaXQgdml0YWUgc2NlbGVyaXNxdWUgZXVpc21vZCwgbmliaCBudWxs +YSBpbnRlcmR1bSBhcmN1LCB2ZWwgdnVscHV0YXRlIGZlbGlzIGVzdCBpbiB0b3J0 +b3IuIFNlZCB1dCB1cm5hIGZlcm1lbnR1bSBkaWFtIGVsZWlmZW5kIGxvYm9ydGlz +LgoKQWxpcXVhbSBub24gbnVuYyBvZGlvLiBBbGlxdWFtIGlkIGRpYW0gdmVsIG1p +IHBvc3VlcmUgYXVjdG9yLiBQcmFlc2VudCB0ZW1wb3IgdGVsbHVzIHF1aXMgb2Rp +byB0aW5jaWR1bnQgcG9zdWVyZS4gSW50ZWdlciBhIG1hc3NhIHB1cnVzLCBhYyBn +cmF2aWRhIG1pLiBEb25lYyB2YXJpdXMgbmVxdWUgZXUgZXJvcyBzY2VsZXJpc3F1 +ZSBibGFuZGl0LiBNYWVjZW5hcyBjb25kaW1lbnR1bSB2b2x1dHBhdCBvZGlvIHBy +ZXRpdW0gY29udmFsbGlzLiBEdWlzIHBvc3VlcmUgdmVoaWN1bGEgdm9sdXRwYXQu +IFNlZCB0aW5jaWR1bnQgc2FwaWVuIGV1IGRpYW0gY3Vyc3VzIHZpdGFlIG9ybmFy +ZSBxdWFtIGFkaXBpc2NpbmcuIE5hbSBmYXVjaWJ1cyB0aW5jaWR1bnQgZWxlaWZl +bmQuIEFlbmVhbiBmYWNpbGlzaXMgY29uc2VxdWF0IHR1cnBpcywgYWMgbG9ib3J0 +aXMgdmVsaXQgZGljdHVtIHF1aXMuIE1hdXJpcyBpbXBlcmRpZXQgY29udmFsbGlz +IGxpZ3VsYSBtYXR0aXMgY29uZ3VlLiBOdWxsYW0gdWx0cmljaWVzIGNvbmd1ZSBs +YWN1cyBldCBwcmV0aXVtLiBQcmFlc2VudCBlcm9zIGFyY3UsIGxvYm9ydGlzIHV0 +IHBvcnR0aXRvciB1dCwgaW50ZXJkdW0gZXUgbGliZXJvLiBOYW0gbmVjIG9yY2kg +ZXJhdC4gQ3JhcyBoZW5kcmVyaXQgY29uc2VjdGV0dXIgbWFzc2EsIGlkIGxvYm9y +dGlzIGFyY3UgZXVpc21vZCBpZC4gU3VzcGVuZGlzc2UgcG90ZW50aS4gTnVsbGFt +IGVnZXN0YXMsIGVyb3MgYWMgc3VzY2lwaXQgbW9sZXN0aWUsIG5lcXVlIHRlbGx1 +cyBhZGlwaXNjaW5nIG1hdXJpcywgbm9uIGNvbW1vZG8gZmVsaXMgb2RpbyB1dCBz +ZW0uIE51bGxhbSBzYWdpdHRpcyBncmF2aWRhIHByZXRpdW0uIEZ1c2NlIHJ1dHJ1 +bSBjdXJzdXMgc2NlbGVyaXNxdWUuIFF1aXNxdWUgY29udmFsbGlzLCBkb2xvciBp +biBwdWx2aW5hciBhbGlxdWV0LCBzYXBpZW4gaXBzdW0gdGVtcG9yIGR1aSwgaWQg +dHJpc3RpcXVlIGVzdCBtYXVyaXMgYXVjdG9yIGp1c3RvLgoKQWVuZWFuIGFsaXF1 +ZXQgbWV0dXMgZXUgbWFnbmEgdHJpc3RpcXVlIGV1IGZlcm1lbnR1bSBtYWduYSB1 +bGxhbWNvcnBlci4gQWVuZWFuIHNjZWxlcmlzcXVlIGJsYW5kaXQgZWxlaWZlbmQu +IEludGVnZXIgYSBvcm5hcmUgb2Rpby4gTWFlY2VuYXMgZWxlaWZlbmQgaGVuZHJl +cml0IGFudGUgaWQgbW9sZXN0aWUuIE1hdXJpcyByaG9uY3VzIHBsYWNlcmF0IGFs +aXF1YW0uIFN1c3BlbmRpc3NlIGEgZWxpdCBkaWFtLCBldCBwaGFyZXRyYSBlc3Qu +IER1aXMgZWxlbWVudHVtIG9yY2kgZXUgYW50ZSBtYXR0aXMgYWMgY29uZ3VlIG5p +c2wgYWxpcXVldC4gSW50ZWdlciBzaXQgYW1ldCBkb2xvciBldCBtYXVyaXMgZWxl +aWZlbmQgbGFvcmVldC4gU2VkIGFsaXF1ZXQgcHJldGl1bSBudWxsYSwgaW4gZWxl +aWZlbmQgYXVndWUgcnV0cnVtIHF1aXMuIFF1aXNxdWUgZGFwaWJ1cyBuaXNsIHZp +dGFlIHRlbGx1cyBpbnRlcmR1bSBldSBibGFuZGl0IGFyY3UgY29uc2VxdWF0LiBF +dGlhbSBzZWQgcHVydXMgcmlzdXMuIEZ1c2NlIHR1cnBpcyBsaWJlcm8sIGFsaXF1 +YW0gbmVjIGxhY2luaWEgYXQsIGJsYW5kaXQgbmVjIG51bmMuIFByYWVzZW50IHZp +dmVycmEsIGxvcmVtIGFjIHBvcnR0aXRvciBtYWxlc3VhZGEsIGxvcmVtIHZlbGl0 +IHNvbGxpY2l0dWRpbiB2ZWxpdCwgc2l0IGFtZXQgdGluY2lkdW50IHVybmEgZHVp +IHZpdGFlIGxlby4KCkRvbmVjIHZpdGFlIGZlbGlzIHF1aXMgZW5pbSB2dWxwdXRh +dGUgcmhvbmN1cy4gUHJhZXNlbnQgZWxlbWVudHVtIHRyaXN0aXF1ZSBjb25ndWUu +IERvbmVjIHF1aXMgbWV0dXMgbmliaC4gTWFlY2VuYXMgYXJjdSBhbnRlLCBsYWNp +bmlhIGluIGRhcGlidXMgaWQsIGZlcm1lbnR1bSBzZWQgZXJhdC4gTW9yYmkgYWRp +cGlzY2luZyBncmF2aWRhIG1hZ25hLCB1dCBwb3N1ZXJlIG9kaW8gZmV1Z2lhdCB2 +aXRhZS4gU3VzcGVuZGlzc2UgcG9ydGEgbHVjdHVzIGxhb3JlZXQuIEFsaXF1YW0g +aW4gZXJhdCBzaXQgYW1ldCBvZGlvIHZlaGljdWxhIG9ybmFyZSBxdWlzIGF0IGR1 +aS4gU3VzcGVuZGlzc2Ugbm9uIGxvcmVtIHZpdGFlIGxpZ3VsYSBsdWN0dXMgY29u +c2VjdGV0dXIgYWMgaWQgcHVydXMuIE51bmMgdXQgbmlzaSBqdXN0by4gTnVsbGFt +IGVnZXQgc2FwaWVuIGNvbW1vZG8gZHVpIHN1c2NpcGl0IHBlbGxlbnRlc3F1ZS4g +Vml2YW11cyBzZW1wZXIgbWkgc2VkIGFyY3Ugc2VtcGVyIHZhcml1cy4gQ3JhcyBh +bGlxdWV0IHZlc3RpYnVsdW0gc2FwaWVuLCBhIGludGVyZHVtIG1pIHVsbGFtY29y +cGVyIHZlbC4gTW9yYmkgcHVydXMgZGlhbSwgb3JuYXJlIG5vbiB0ZW1wb3Igbm9u +LCBjb252YWxsaXMgbmVjIGVuaW0uIE51bGxhbSB0cmlzdGlxdWUgdnVscHV0YXRl +IGxlbyBuZWMgZmFjaWxpc2lzLiBQZWxsZW50ZXNxdWUgaGFiaXRhbnQgbW9yYmkg +dHJpc3RpcXVlIHNlbmVjdHVzIGV0IG5ldHVzIGV0IG1hbGVzdWFkYSBmYW1lcyBh +YyB0dXJwaXMgZWdlc3Rhcy4KCk1vcmJpIGltcGVyZGlldCB2ZWhpY3VsYSBjb25n +dWUuIE1hdXJpcyBxdWlzIG51bGxhIG9kaW8sIGEgZmF1Y2lidXMgdXJuYS4gTnVs +bGEgZXQgZXJvcyBkb2xvci4gRnVzY2UgYSBmYXVjaWJ1cyBpcHN1bS4gTWF1cmlz +IGZlcm1lbnR1bSBmZXVnaWF0IGRpZ25pc3NpbS4gVXQgYSBqdXN0byBlbGl0LCBu +b24gYXVjdG9yIHF1YW0uIFNlZCBsYWN1cyBhdWd1ZSwgcGxhY2VyYXQgYXQgYWxp +cXVhbSBub24sIG1hdHRpcyBpZCBtaS4gQWVuZWFuIGEgbGFvcmVldCBlcm9zLiBD +cmFzIHVsdHJpY2llcyBlbGl0IGluIG51bmMgc3VzY2lwaXQgaWQgc29sbGljaXR1 +ZGluIGVuaW0gY29uZGltZW50dW0uIFBoYXNlbGx1cyBkaWduaXNzaW0gbmlzaSBx +dWlzIGxlbyBpbnRlcmR1bSBwaGFyZXRyYS4gRG9uZWMgc2l0IGFtZXQgbGVvIGEg +ZHVpIHZpdmVycmEgbW9sZXN0aWUuIEN1cmFiaXR1ciBsb2JvcnRpcyBzYXBpZW4g +cXVpcyBuZXF1ZSBwdWx2aW5hciBhdCBmcmluZ2lsbGEgbmlzaSB2ZWhpY3VsYS4K +ClByYWVzZW50IG5vbiBwdXJ1cyB0ZWxsdXMuIEludGVnZXIgaWFjdWxpcyBkYXBp +YnVzIG5pc2wsIHRyaXN0aXF1ZSB2ZXN0aWJ1bHVtIG1pIG1hbGVzdWFkYSBzaXQg +YW1ldC4gU2VkIGxvYm9ydGlzIGhlbmRyZXJpdCBsb2JvcnRpcy4gUXVpc3F1ZSB0 +ZW1wb3IsIGFyY3UgYWMgdGVtcHVzIGF1Y3RvciwgbmVxdWUgcXVhbSBkaWN0dW0g +cXVhbSwgdml0YWUgZ3JhdmlkYSBsaWd1bGEgbmliaCBldSBhdWd1ZS4gQWVuZWFu +IGV1aXNtb2QgdGVtcG9yIGlwc3VtIGEgaGVuZHJlcml0LiBDcmFzIGRvbG9yIGxp +Z3VsYSwgZmF1Y2lidXMgYWMgaGVuZHJlcml0IGVnZXQsIGFsaXF1YW0gYSBuaXNs +LiBJbnRlZ2VyIG5lYyBjb25zZXF1YXQganVzdG8uIEludGVnZXIgcGhhcmV0cmEg +c2NlbGVyaXNxdWUgbGVvLiBBZW5lYW4gZXVpc21vZCB2ZWhpY3VsYSBhbnRlIG5v +biBwaGFyZXRyYS4gVmVzdGlidWx1bSBzb2xsaWNpdHVkaW4ganVzdG8gZXUgc2Vt +IGxhb3JlZXQgaW4gdGVtcHVzIG5lcXVlIGNvbW1vZG8uCgpVdCBpbiBhcmN1IGFu +dGUuIEN1cmFiaXR1ciB1bHRyaWNpZXMgdmVsaXQgZGlhbS4gQWxpcXVhbSB2ZWxp +dCBlcm9zLCB2b2x1dHBhdCBxdWlzIGN1cnN1cyBpZCwgdmVoaWN1bGEgYWMgdG9y +dG9yLiBOdWxsYSBwcmV0aXVtLCBlcmF0IGlkIGZhY2lsaXNpcyBibGFuZGl0LCBy +aXN1cyB0ZWxsdXMgYWxpcXVldCBvcmNpLCB2ZWwgY29uZGltZW50dW0gbmliaCBl +bGl0IHZpdGFlIGVsaXQuIFBoYXNlbGx1cyB1bGxhbWNvcnBlciBjb25zZWN0ZXR1 +ciBhbnRlIHNpdCBhbWV0IGx1Y3R1cy4gTnVsbGEgdXQgbnVsbGEgZWdldCBlcm9z +IGRpY3R1bSBwZWxsZW50ZXNxdWUgdmVsIHV0IG51bGxhLiBEb25lYyBsYW9yZWV0 +IHZpdmVycmEgbWFnbmEgdXQgaW50ZXJkdW0uIFZpdmFtdXMgcHVsdmluYXIgc2Fw +aWVuIGNvbnZhbGxpcyBtYWduYSBzb2RhbGVzIGVnZXQgZWdlc3RhcyBlc3QgaW1w +ZXJkaWV0LiBRdWlzcXVlIGZhY2lsaXNpcyBhbGlxdWFtIHNvZGFsZXMuIEV0aWFt +IGZlcm1lbnR1bSwgb2RpbyBhYyBzZW1wZXIgc2NlbGVyaXNxdWUsIHB1cnVzIGVu +aW0gaW1wZXJkaWV0IGF1Z3VlLCBzaXQgYW1ldCBkaWduaXNzaW0gc2FwaWVuIHRv +cnRvciBuZWMgbWF1cmlzLiBTdXNwZW5kaXNzZSBlbGVpZmVuZCBjb21tb2RvIG51 +bmMgcXVpcyBjb25zZWN0ZXR1ci4gRG9uZWMgZWdldCB0cmlzdGlxdWUgbGVvLgoK +U3VzcGVuZGlzc2Ugc29sbGljaXR1ZGluIHBoYXJldHJhIHNlbXBlci4gU2VkIGp1 +c3RvIGVzdCwgZmF1Y2lidXMgaWQgY3Vyc3VzIGEsIHBlbGxlbnRlc3F1ZSBpbiBt +aS4gTWF1cmlzIHNhZ2l0dGlzLCBpcHN1bSBjdXJzdXMgYmxhbmRpdCBpbXBlcmRp +ZXQsIGVzdCBlc3QgbHVjdHVzIHJpc3VzLCBxdWlzIGZlcm1lbnR1bSBsZW8gb3Jj +aSBhIG9kaW8uIE51bmMgc2l0IGFtZXQgdmVsaXQgaXBzdW0uIE5hbSBldWlzbW9k +IHB1bHZpbmFyIG1ldHVzLCBhYyBhZGlwaXNjaW5nIGR1aSB1bGxhbWNvcnBlciBh +LiBWZXN0aWJ1bHVtIGFudGUgaXBzdW0gcHJpbWlzIGluIGZhdWNpYnVzIG9yY2kg +bHVjdHVzIGV0IHVsdHJpY2VzIHBvc3VlcmUgY3ViaWxpYSBDdXJhZTsgU2VkIG1v +bGVzdGllIHRpbmNpZHVudCBkb2xvciwgc2VkIHByZXRpdW0gbWFnbmEgY29uc2Vj +dGV0dXIgdXQuIExvcmVtIGlwc3VtIGRvbG9yIHNpdCBhbWV0LCBjb25zZWN0ZXR1 +ciBhZGlwaXNjaW5nIGVsaXQuIEFsaXF1YW0gbGVjdHVzIGxpZ3VsYSwgaW50ZXJk +dW0gYXQgbG9ib3J0aXMgaWQsIGNvbnZhbGxpcyBzaXQgYW1ldCBzZW0uIFNlZCBl +Z2V0IG5lcXVlIGluIGFyY3UgdWx0cmljZXMgZmV1Z2lhdC4gUHJvaW4gdnVscHV0 +YXRlIGxlbyBlZ2V0IGVyb3MgZGFwaWJ1cyBhbGlxdWV0LiBGdXNjZSBmYWNpbGlz +aXMgY29uc2VxdWF0IGRpYW0sIHNlZCB2ZXN0aWJ1bHVtIG1pIHVsdHJpY2llcyBl +Z2V0LiBVdCBwb3N1ZXJlIGNvbmd1ZSBsaWJlcm8gc2l0IGFtZXQgZGlnbmlzc2lt +LiBQcm9pbiBxdWlzIGFyY3UgbGliZXJvLgoKVmVzdGlidWx1bSBhdCBhdWd1ZSBt +YXVyaXMuIEFsaXF1YW0gc2FnaXR0aXMgcG9ydGEgYW50ZSBpbiB2YXJpdXMuIFZl +c3RpYnVsdW0gbHVjdHVzIGlhY3VsaXMgYXJjdSBhIGludGVyZHVtLiBVdCB2YXJp +dXMgaGVuZHJlcml0IGxhY3VzIG5lYyBzYWdpdHRpcy4gU2VkIHV0IHJpc3VzIGFj +IGxlY3R1cyB1bHRyaWNpZXMgbW9sbGlzLiBOdW5jIGZldWdpYXQgc3VzY2lwaXQg +bnVuYyBxdWlzIGVsZW1lbnR1bS4gU3VzcGVuZGlzc2UgcG90ZW50aS4gRnVzY2Ug +bGFvcmVldCBlbGl0IGV1IG9kaW8gZnJpbmdpbGxhIHBlbGxlbnRlc3F1ZS4gRXRp +YW0gdml0YWUgYXJjdSBxdWlzIHF1YW0gc29kYWxlcyBmYWNpbGlzaXMgY29uc2Vx +dWF0IHV0IG9yY2kuIFBoYXNlbGx1cyB2aXRhZSBxdWFtIHF1aXMgbGVvIGNvbmd1 +ZSBpbnRlcmR1bSBzZWQgc3VzY2lwaXQgZXN0LiBNYWVjZW5hcyBjb25kaW1lbnR1 +bSwgbWFzc2EgZWdldCB0ZW1wb3IgcGhhcmV0cmEsIGVyYXQgbGVvIGxhY2luaWEg +b2Rpbywgc2l0IGFtZXQgZmV1Z2lhdCBtYXVyaXMgcmlzdXMgc2VkIHNhcGllbi4g +TW9yYmkgYXJjdSBsb3JlbSwgZGFwaWJ1cyBlZ2V0IHZlbmVuYXRpcyBzZWQsIHNl +bXBlciBlZ2V0IGVyb3MuIENyYXMgc2VtcGVyIHVsdHJpY2llcyBlcm9zIHZvbHV0 +cGF0IHZ1bHB1dGF0ZS4gTW9yYmkgYXQgbnVuYyBlZ2V0IG5pYmggZmV1Z2lhdCB0 +ZW1wdXMuCgpJbiBlZ2V0IGVsaXQgYSBkaWFtIHBsYWNlcmF0IHZvbHV0cGF0IHNl +ZCB0ZW1wdXMgZGlhbS4gTWFlY2VuYXMgdGVtcG9yIHNlbSBpZCBzZW0gdml2ZXJy +YSB2ZWwgZmVybWVudHVtIHJpc3VzIHBvcnR0aXRvci4gUHJvaW4gYSBtZXR1cyBt +ZXR1cy4gQWVuZWFuIHF1aXMgdGluY2lkdW50IG1hZ25hLiBEb25lYyBzaXQgYW1l +dCBwb3N1ZXJlIHJpc3VzLiBEb25lYyBlc3QganVzdG8sIGRhcGlidXMgaW4gc29s +bGljaXR1ZGluIGV1LCB2ZW5lbmF0aXMgc2l0IGFtZXQgZWxpdC4gQ2xhc3MgYXB0 +ZW50IHRhY2l0aSBzb2Npb3NxdSBhZCBsaXRvcmEgdG9ycXVlbnQgcGVyIGNvbnVi +aWEgbm9zdHJhLCBwZXIgaW5jZXB0b3MgaGltZW5hZW9zLiBDcmFzIGluIHR1cnBp +cyBuZWMgbnVuYyBmcmluZ2lsbGEgaWFjdWxpcy4gRXRpYW0gc3VzY2lwaXQgbmli +aCB2aXRhZSBsaWJlcm8gcGVsbGVudGVzcXVlIHZpdmVycmEuIENyYXMgaWQgbGVj +dHVzIHF1aXMgZW5pbSBvcm5hcmUgbW9sZXN0aWUgdml0YWUgdmVsIHRlbGx1cy4g +SW4gdmVzdGlidWx1bSB2dWxwdXRhdGUgdHVycGlzIGlkIHBvc3VlcmUuCgpOYW0g +ZXN0IGVyYXQsIHJob25jdXMgbm9uIGltcGVyZGlldCBpbiwgc2FnaXR0aXMgdml0 +YWUgaXBzdW0uIE51bGxhIHJ1dHJ1bSB0aW5jaWR1bnQgbGVjdHVzIGV0IGJsYW5k +aXQuIFV0IHNlZCBsaWd1bGEgbmlzbCwgbm9uIGRpY3R1bSBlcmF0LiBBZW5lYW4g +bG9yZW0gZW5pbSwgbW9sbGlzIHV0IHRpbmNpZHVudCBpZCwgY29tbW9kbyBzaXQg +YW1ldCBuaXNsLiBNYWVjZW5hcyBibGFuZGl0IHJob25jdXMgc2VtcGVyLiBGdXNj +ZSBhIG1hc3NhIG9yY2ksIGV0IHZlbmVuYXRpcyBhcmN1LiBNYXVyaXMgbW9sbGlz +IGR1aSBxdWlzIGZlbGlzIGJpYmVuZHVtIHBoYXJldHJhLiBDcmFzIHNlZCBpcHN1 +bSBtYXNzYSwgcG9ydHRpdG9yIHB1bHZpbmFyIG5pc2kuIExvcmVtIGlwc3VtIGRv +bG9yIHNpdCBhbWV0LCBjb25zZWN0ZXR1ciBhZGlwaXNjaW5nIGVsaXQuIFZpdmFt +dXMgY3Vyc3VzIGVsaXQgc2FnaXR0aXMgbGliZXJvIGhlbmRyZXJpdCB2ZXN0aWJ1 +bHVtLiBTZWQgcG9ydHRpdG9yLCB0b3J0b3IgdGluY2lkdW50IHBvcnR0aXRvciBz +ZW1wZXIsIGRpYW0gbWFnbmEgZWxlaWZlbmQgbnVuYywgc2VkIGNvbmRpbWVudHVt +IG5pYmggZXJhdCBxdWlzIG9kaW8uIERvbmVjIHBvc3VlcmUgdmVoaWN1bGEgcHVy +dXMsIGluIHB1bHZpbmFyIG9kaW8gaW50ZXJkdW0gZXUuIFZlc3RpYnVsdW0gZmFj +aWxpc2lzIHRpbmNpZHVudCBkYXBpYnVzLiBGdXNjZSBsdWN0dXMgbG9yZW0gZWdl +dCBxdWFtIGFjY3Vtc2FuIGluIG1hdHRpcyBuaWJoIHZvbHV0cGF0LiBOdWxsYW0g +ZXQgbGVvIGEgdXJuYSBwb3J0YSB2YXJpdXMgbm9uIHZpdGFlIGR1aS4gU2VkIGlu +dGVyZHVtLCBtZXR1cyBldSBydXRydW0gcGhhcmV0cmEsIG5pc2wgc2VtIHRlbXB1 +cyBqdXN0bywgdmFyaXVzIGNvbnZhbGxpcyBpcHN1bSBkaWFtIHV0IGxlY3R1cy4K +CkluIGhhYyBoYWJpdGFzc2UgcGxhdGVhIGRpY3R1bXN0LiBWZXN0aWJ1bHVtIGNv +bmd1ZSBzb2RhbGVzIG5pc2kgcXVpcyBvcm5hcmUuIEZ1c2NlIGN1cnN1cyBuaXNp +IGF0IHR1cnBpcyBjb25ndWUgaGVuZHJlcml0LiBWZXN0aWJ1bHVtIG5vbiBsYWN1 +cyB2ZWwgc2FwaWVuIHB1bHZpbmFyIHZlaGljdWxhIGF0IGluIGFudGUuIE51bGxh +IGZhY2lsaXNpLiBVdCB0cmlzdGlxdWUgdGluY2lkdW50IGVyYXQgaWQgbHVjdHVz +LiBDdXJhYml0dXIgcG9zdWVyZSBzb2RhbGVzIG5lcXVlIHF1aXMgdmFyaXVzLiBD +dXJhYml0dXIgY3Vyc3VzIGFjY3Vtc2FuIGlwc3VtLCB2ZWwgbHVjdHVzIGxpZ3Vs +YSBkYXBpYnVzIG5vbi4gQ3JhcyB2ZWhpY3VsYSBtYWduYSBpbiBsYWN1cyBvcm5h +cmUgZGFwaWJ1cy4gRG9uZWMgYWxpcXVldCBzb2xsaWNpdHVkaW4gbGFjdXMsIGV1 +IGlhY3VsaXMgZXJhdCBiaWJlbmR1bSBjb21tb2RvLiBEb25lYyBibGFuZGl0IGJp +YmVuZHVtIGZldWdpYXQuIFByYWVzZW50IGV1IGVzdCBqdXN0by4gUGVsbGVudGVz +cXVlIGF0IGVuaW0gc2VkIHNlbSB2aXZlcnJhIGNvbnZhbGxpcy4gTWFlY2VuYXMg +dmVuZW5hdGlzIG1ldHVzIHNhcGllbi4gU2VkIHBsYWNlcmF0IGZhY2lsaXNpcyBl +bGl0IG5lYyBtYWxlc3VhZGEuIEZ1c2NlIHNhcGllbiBlc3QsIGNvbnNlcXVhdCBh +IGNvbmd1ZSBlZ2V0LCBhY2N1bXNhbiBlZ2V0IGxvcmVtLiBTdXNwZW5kaXNzZSB0 +aW5jaWR1bnQgcHJldGl1bSBtYWduYSBlZ2V0IGRhcGlidXMuIEZ1c2NlIHZlbCBl +cm9zIGV0IGxvcmVtIGNvbnNlcXVhdCB0cmlzdGlxdWUgbmVjIGEgaXBzdW0uCgpQ +cm9pbiBxdWlzIGN1cnN1cyBhcmN1LiBNYWVjZW5hcyBlbGVpZmVuZCBsb3JlbSBp +ZCBuaXNsIHNjZWxlcmlzcXVlIHBsYWNlcmF0LiBGdXNjZSBpbXBlcmRpZXQgbG9y +ZW0gZXUgdXJuYSBkaWduaXNzaW0gZmVybWVudHVtLiBQZWxsZW50ZXNxdWUgbnVu +YyBuaXNsLCBpbXBlcmRpZXQgdXQgYWNjdW1zYW4gaWQsIGZlcm1lbnR1bSBub24g +bWF1cmlzLiBBZW5lYW4gZnJpbmdpbGxhIGxlY3R1cyB2aXRhZSB0dXJwaXMgZmVy +bWVudHVtIHZpdGFlIGZhY2lsaXNpcyBvZGlvIG1vbGxpcy4gQWVuZWFuIG5vbiBz +ZW0gZXQgZXJvcyBjb25ndWUgcG9ydGEgaWQgc2l0IGFtZXQgZGlhbS4gRG9uZWMg +ZnJpbmdpbGxhIGVyb3MgYXQgcXVhbSBpbXBlcmRpZXQgYXQgZ3JhdmlkYSB0b3J0 +b3IgdWxsYW1jb3JwZXIuIEZ1c2NlIGRvbG9yIHJpc3VzLCB2aXZlcnJhIGlkIGFs +aXF1ZXQgc2VkLCBkaWduaXNzaW0gcXVpcyBtYXVyaXMuIFF1aXNxdWUgYWMgbWV0 +dXMgaWQgcXVhbSBsb2JvcnRpcyB0cmlzdGlxdWUgYXQgdmVsIGVuaW0uIFBoYXNl +bGx1cyBpbiB0b3J0b3IgbWF1cmlzLiBTZWQgdGluY2lkdW50IGR1aSBub24gZXJv +cyBzb2xsaWNpdHVkaW4gdm9sdXRwYXQuIEN1cmFiaXR1ciB2aXZlcnJhIGVsZW1l +bnR1bSBhcmN1IGFjIHVsdHJpY2VzLiBOdWxsYSBmZWxpcyBsaWd1bGEsIGF1Y3Rv +ciBhdCBsYWNpbmlhIHNlZCwgdGluY2lkdW50IGF0IG1pLiBJbiB2ZWwgZWxpdCBv +cmNpLiBWZXN0aWJ1bHVtIGVyYXQgbmlzaSwgbW9sZXN0aWUgdmVsIGF1Y3RvciB2 +ZWwsIHNhZ2l0dGlzIGFjIGxlY3R1cy4gUGVsbGVudGVzcXVlIHVsdHJpY2llcyBj +b25kaW1lbnR1bSBudWxsYSBuZWMgZXVpc21vZC4KCkRvbmVjIGNvbnNlY3RldHVy +IHZlbmVuYXRpcyBzZW1wZXIuIFN1c3BlbmRpc3NlIHZlbCBkaWN0dW0gYXVndWUu +IFBlbGxlbnRlc3F1ZSBub24gbWF1cmlzIGp1c3RvLiBEb25lYyBhY2N1bXNhbiwg +bWV0dXMgdXQgcHJldGl1bSBtb2xlc3RpZSwgbGlndWxhIGp1c3RvIGx1Y3R1cyBv +cmNpLCBub24gdmFyaXVzIG1pIG5pYmggZXUganVzdG8uIFZlc3RpYnVsdW0gc2Vt +IHNlbSwgdGluY2lkdW50IGVnZXQgcG9ydGEgYSwgZmVybWVudHVtIGV0IHNhcGll +bi4gTnVsbGEgZmFjaWxpc2kuIFBlbGxlbnRlc3F1ZSBpbiB2ZXN0aWJ1bHVtIGFu +dGUuIE51bmMgaWFjdWxpcyBsaWd1bGEgbmVjIG9kaW8gc2FnaXR0aXMgbmVjIHBv +cnR0aXRvciBuaWJoIHNlbXBlci4gVXQgZWdldCBtZXR1cyBvcmNpLiBQcm9pbiBn +cmF2aWRhIG9yY2kgZGlnbmlzc2ltIGRpYW0gaWFjdWxpcyBhY2N1bXNhbi4gQWVu +ZWFuIGF1Z3VlIG9yY2ksIHBsYWNlcmF0IGV0IGNvbnZhbGxpcyBlZ2V0LCBzb2Rh +bGVzIHF1aXMgZGlhbS4gU2VkIHN1c2NpcGl0IG5pc2kgcXVpcyBsaWJlcm8gc29s +bGljaXR1ZGluIGV1IG1vbGxpcyBuZXF1ZSBtb2xlc3RpZS4gTWFlY2VuYXMgcHVy +dXMgbmlzbCwgY29uc2VjdGV0dXIgbm9uIHZlaGljdWxhIHNpdCBhbWV0LCB1bGxh +bWNvcnBlciBxdWlzIG5pYmguIFByYWVzZW50IHF1YW0gaXBzdW0sIG1vbGVzdGll +IGV1IHB1bHZpbmFyIGF0LCBpYWN1bGlzIGV1IHR1cnBpcy4gQ3JhcyBuZWMgZWxp +dCB1dCBsaWJlcm8gc3VzY2lwaXQgYWxpcXVldCBldCBuZWMgbGVjdHVzLiBWZXN0 +aWJ1bHVtIHB1cnVzIGF1Z3VlLCB0cmlzdGlxdWUgdml0YWUgY29udmFsbGlzIGF0 +LCB2ZWhpY3VsYSBub24gbWV0dXMuIEZ1c2NlIGx1Y3R1cyBjb25ndWUgbWksIHZl +bCBzYWdpdHRpcyBkb2xvciBtb2xsaXMgdmVsLiBQcmFlc2VudCBpbiBudW5jIGFj +Y3Vtc2FuIGxvcmVtIGxvYm9ydGlzIGlhY3VsaXMgdXQgdml0YWUgaXBzdW0uIFN1 +c3BlbmRpc3NlIHB1bHZpbmFyLCBudW5jIGFjIGFsaXF1ZXQgc2VtcGVyLCBsYWN1 +cyBsb3JlbSBldWlzbW9kIHB1cnVzLCB2ZWwgbW9sbGlzIG1ldHVzIHJpc3VzIG5l +YyBzYXBpZW4uCgpJbiB2ZW5lbmF0aXMgcG9zdWVyZSBzYXBpZW4sIHNpdCBhbWV0 +IGV1aXNtb2QgZG9sb3IgcGVsbGVudGVzcXVlIGV1LiBQcmFlc2VudCBsYW9yZWV0 +IGZlbGlzIHV0IG1ldHVzIHZlc3RpYnVsdW0gY29uZGltZW50dW0uIFV0IHNjZWxl +cmlzcXVlIGxlbyBhdWd1ZS4gU3VzcGVuZGlzc2Ugdml0YWUgZG9sb3IgcHVydXMs +IGlkIHJ1dHJ1bSBhbnRlLiBGdXNjZSBpbiB2ZWxpdCBhbnRlLCBhIHBlbGxlbnRl +c3F1ZSBtaS4gTWFlY2VuYXMgdmVsIHJpc3VzIGVnZXQgb3JjaSBmZXJtZW50dW0g +cHJldGl1bSBuZWMgdmVsIHNhcGllbi4gUGhhc2VsbHVzIGlwc3VtIG1hZ25hLCBi +aWJlbmR1bSBxdWlzIGZhdWNpYnVzIHZlbCwgcGxhY2VyYXQgaWQgbWFnbmEuIFZp +dmFtdXMgcXVhbSBkdWksIGNvbnNlY3RldHVyIHNlZCBwdWx2aW5hciBldSwgaW50 +ZXJkdW0gaWQgaXBzdW0uIENyYXMgbG9ib3J0aXMgZmFjaWxpc2lzIHJ1dHJ1bS4g +Q3VtIHNvY2lpcyBuYXRvcXVlIHBlbmF0aWJ1cyBldCBtYWduaXMgZGlzIHBhcnR1 +cmllbnQgbW9udGVzLCBuYXNjZXR1ciByaWRpY3VsdXMgbXVzLiBOYW0gdmVzdGli +dWx1bSBudW5jIHNlZCBqdXN0byBkaWduaXNzaW0gYWMgdGluY2lkdW50IHNhcGll +biBjb25ndWUuCgpQaGFzZWxsdXMgbm9uIGRpY3R1bSB0dXJwaXMuIFByb2luIGVn +ZXQgbWFzc2EgbGFjaW5pYSBsaWJlcm8gcnV0cnVtIHZlc3RpYnVsdW0uIE51bGxh +IGFsaXF1ZXQgbGliZXJvIGlkIHNhcGllbiBhbGlxdWFtIGZlcm1lbnR1bS4gVml2 +YW11cyBsZWN0dXMgaXBzdW0sIHBoYXJldHJhIHV0IGRhcGlidXMgdXQsIGFsaXF1 +YW0gYWMgbmVxdWUuIFZpdmFtdXMgZmF1Y2lidXMgbWkgcXVpcyBtYXVyaXMgcnV0 +cnVtIGFjIHBsYWNlcmF0IG5pc2kgc2NlbGVyaXNxdWUuIE1hdXJpcyBlbGl0IG9y +Y2ksIGFkaXBpc2NpbmcgZXUgY29uc2VxdWF0IGlkLCB2ZW5lbmF0aXMgaW4gbnVu +Yy4gQWVuZWFuIG5vbiBibGFuZGl0IGxpYmVyby4gU3VzcGVuZGlzc2UgaWQgYXVn +dWUgZHVpLiBRdWlzcXVlIG5lYyBvcmNpIHZlbCBvZGlvIG1hdHRpcyBncmF2aWRh +LiBWZXN0aWJ1bHVtIGFudGUgaXBzdW0gcHJpbWlzIGluIGZhdWNpYnVzIG9yY2kg +bHVjdHVzIGV0IHVsdHJpY2VzIHBvc3VlcmUgY3ViaWxpYSBDdXJhZTsgTWF1cmlz +IHZvbHV0cGF0IGR1aSB1dCBsYWN1cyBhZGlwaXNjaW5nIGJpYmVuZHVtLiBMb3Jl +bSBpcHN1bSBkb2xvciBzaXQgYW1ldCwgY29uc2VjdGV0dXIgYWRpcGlzY2luZyBl +bGl0LiBWaXZhbXVzIHZlbmVuYXRpcyBqdXN0byBldSBzYXBpZW4gZmVybWVudHVt +IGNvbmd1ZSBxdWlzIGluIG51bmMuIE51bGxhbSBsYWNpbmlhIGVyb3MgbG9yZW0s +IHV0IGFsaXF1YW0gb3JjaS4gUGhhc2VsbHVzIHN1c2NpcGl0LCBsYWN1cyBldCB0 +ZW1wb3Igc2VtcGVyLCBlc3QgbWF1cmlzIGNvbmd1ZSBtYXNzYSwgc2VkIHVsdHJp +Y2VzIG9yY2kgZGlhbSB2aXRhZSBpcHN1bS4KCk51bGxhIGZhY2lsaXNpLiBTZWQg +cG9zdWVyZSBmcmluZ2lsbGEgZmVsaXMsIGFjIHZlbmVuYXRpcyBkdWkgdnVscHV0 +YXRlIHV0LiBQcm9pbiBlbGVpZmVuZCB0ZW1wb3IgbW9sZXN0aWUuIE51bGxhIHRp +bmNpZHVudCBsZWN0dXMgdml0YWUgbmVxdWUgYmliZW5kdW0gcHJldGl1bS4gSW4g +bGliZXJvIG5pc2wsIHRyaXN0aXF1ZSBuZWMgYmliZW5kdW0gc2l0IGFtZXQsIHVs +bGFtY29ycGVyIG5lYyBlcmF0LiBTdXNwZW5kaXNzZSBpbnRlcmR1bSBwb3J0YSBv +cmNpLCBxdWlzIHZpdmVycmEgZXJhdCBhdWN0b3IgYXQuIER1aXMgYWMgZGlhbSBl +dCBsaWd1bGEgYmxhbmRpdCB0ZW1wb3IgbmVjIGV1IGxhY3VzLiBDdW0gc29jaWlz +IG5hdG9xdWUgcGVuYXRpYnVzIGV0IG1hZ25pcyBkaXMgcGFydHVyaWVudCBtb250 +ZXMsIG5hc2NldHVyIHJpZGljdWx1cyBtdXMuIFByYWVzZW50IHBlbGxlbnRlc3F1 +ZSB2YXJpdXMgbnVsbGEgdmVsIGFsaXF1YW0uIEFsaXF1YW0gdXQgbGVjdHVzIG1h +dXJpcywgaWQgYWxpcXVldCBsYWN1cy4gSW50ZWdlciBzY2VsZXJpc3F1ZSBlbGVt +ZW50dW0gZHVpLCBldSBsYW9yZWV0IGF1Z3VlIGVsZW1lbnR1bSBzaXQgYW1ldC4g +VXQgdWxsYW1jb3JwZXIgdGVsbHVzIGNvbnZhbGxpcyBzYXBpZW4gdmVzdGlidWx1 +bSBub24gbGFvcmVldCBwdXJ1cyBhZGlwaXNjaW5nLiBOdW5jIG5lYyBxdWFtIG51 +bmMuIFZpdmFtdXMgdWx0cmljZXMgZmVybWVudHVtIG1hc3NhLCBzaXQgYW1ldCBz +Y2VsZXJpc3F1ZSBudW5jIGltcGVyZGlldCB1dC4KClZlc3RpYnVsdW0gYW50ZSBp +cHN1bSBwcmltaXMgaW4gZmF1Y2lidXMgb3JjaSBsdWN0dXMgZXQgdWx0cmljZXMg +cG9zdWVyZSBjdWJpbGlhIEN1cmFlOyBJbiBoYWMgaGFiaXRhc3NlIHBsYXRlYSBk +aWN0dW1zdC4gTnVsbGFtIHRpbmNpZHVudCBtb2xlc3RpZSBpcHN1bSB2ZWwgcmhv +bmN1cy4gSW50ZWdlciBlZ2V0IG51bmMgZXUgdGVsbHVzIHRpbmNpZHVudCBsYWNp +bmlhLiBJbnRlZ2VyIG5lYyBuaXNpIGVnZXQgYXVndWUgc2VtcGVyIHZhcml1cyBp +ZCBhYyBxdWFtLiBOYW0gdGluY2lkdW50LCB0ZWxsdXMgYWMgY29uZGltZW50dW0g +YWNjdW1zYW4sIGRvbG9yIGVyb3Mgdml2ZXJyYSBsZW8sIHBoYXJldHJhIHB1bHZp +bmFyIGxlY3R1cyBlcm9zIGluIG1pLiBEb25lYyBjb25zZXF1YXQsIHR1cnBpcyBp +ZCBjb252YWxsaXMgdm9sdXRwYXQsIGFudGUgc2VtIGVsZW1lbnR1bSBtYWduYSwg +cXVpcyB0ZW1wb3IgcmlzdXMgZGlhbSB2ZWwgdHVycGlzLiBNb3JiaSBhIG9yY2kg +b3JjaSwgZWdlc3RhcyBmZXVnaWF0IGxlY3R1cy4gTnVsbGEgbmVjIG5pYmggaWQg +YXVndWUgaW50ZXJkdW0gbW9sbGlzLiBTdXNwZW5kaXNzZSBwbGFjZXJhdCB0ZW1w +dXMgbnVuYyBhYyB2ZXN0aWJ1bHVtLiBEb25lYyBldCBtZXR1cyBkaWN0dW0gbGVv +IGJpYmVuZHVtIHVsdHJpY2VzIGluIG5lYyBsZWN0dXMuIFV0IHNlZCB2ZWxpdCBu +aWJoLCBzb2RhbGVzIHBvcnRhIG51bmMuIEluIGhhYyBoYWJpdGFzc2UgcGxhdGVh +IGRpY3R1bXN0LiBTZWQgZWxpdCB0b3J0b3IsIGFsaXF1ZXQgdmVsIHZvbHV0cGF0 +IGEsIHRpbmNpZHVudCBzZWQgc2FwaWVuLiBTdXNwZW5kaXNzZSBlZ2V0IGxpYmVy +byBsYWN1cy4gVml2YW11cyBhdWN0b3IgYW50ZSBub24gbmVxdWUgcG9ydHRpdG9y +IHV0IHByZXRpdW0gbmlzbCB0ZW1wdXMuIE51bGxhIHNvZGFsZXMgcmlzdXMgZXQg +ZXJhdCB2YXJpdXMgbGFvcmVldC4gTWFlY2VuYXMgbHVjdHVzIGxvYm9ydGlzIGxp +Z3VsYSBlZ2V0IGZlcm1lbnR1bS4KClZpdmFtdXMgY29udmFsbGlzIG5pYmggdml0 +YWUgdHVycGlzIGZldWdpYXQgdWx0cmljZXMuIFF1aXNxdWUgdml0YWUgYmxhbmRp +dCBtYXNzYS4gTWF1cmlzIGV1aXNtb2QgdXJuYSB1dCBuZXF1ZSBmZXJtZW50dW0g +dml0YWUgaW1wZXJkaWV0IG1hdXJpcyBldWlzbW9kLiBJbiBhIGR1aSB0ZWxsdXMs +IG5lYyBwb3N1ZXJlIGRvbG9yLiBBZW5lYW4gdWxsYW1jb3JwZXIgYXVndWUgaWQg +ZXJhdCBsYWNpbmlhIHB1bHZpbmFyLiBQZWxsZW50ZXNxdWUgYXQgbnVsbGEgdGVs +bHVzLiBOYW0gZWxlbWVudHVtIGlhY3VsaXMgcHVsdmluYXIuIFZpdmFtdXMgdG9y +dG9yIGxlY3R1cywgZ3JhdmlkYSB1dCBjb21tb2RvIGEsIHZpdmVycmEgdXQgb2Rp +by4gUGhhc2VsbHVzIGVsZW1lbnR1bSBibGFuZGl0IG9kaW8sIG5lYyBkYXBpYnVz +IGFudGUgbWFsZXN1YWRhIGEuIE1hZWNlbmFzIGltcGVyZGlldCwgZmVsaXMgYSBz +ZW1wZXIgZmFjaWxpc2lzLCBtYXNzYSBudWxsYSBhbGlxdWV0IHRvcnRvciwgZXQg +bWFsZXN1YWRhIG1hZ25hIGxlY3R1cyBhYyBwdXJ1cy4gQWVuZWFuIGF1Y3RvciB0 +ZWxsdXMgaWQganVzdG8gY29uZGltZW50dW0gdHJpc3RpcXVlLiBDcmFzIHZpdGFl +IHJ1dHJ1bSBhcmN1LiBNYXVyaXMgdGVtcHVzLCBtYWduYSBlZ2V0IHZhcml1cyBp +bnRlcmR1bSwgbmlzbCBqdXN0byBwb3J0YSBhdWd1ZSwgc2VkIHBvcnRhIG5pc2wg +cXVhbSBhIG5pYmguIE1hZWNlbmFzIHZpdGFlIHB1cnVzIGRpYW0uIFByYWVzZW50 +IGJpYmVuZHVtIG5pYmggZHVpLCBpbiB0aW5jaWR1bnQgbnVuYy4gRHVpcyB2aXRh +ZSBzZW0gYW50ZS4KCk1hdXJpcyBwb3N1ZXJlIGZlbGlzIGV0IHR1cnBpcyBzb2Rh +bGVzIGhlbmRyZXJpdCB1dCBpZCBudW5jLiBEb25lYyBsYW9yZWV0IG1hbGVzdWFk +YSBlcm9zLiBOYW0gcXVpcyBkaWFtIGFyY3UsIGV1IHRyaXN0aXF1ZSB0ZWxsdXMu +IFBlbGxlbnRlc3F1ZSBxdWlzIGVsaXQgbnVuYy4gQ3JhcyBsZW8gbG9yZW0sIG9y +bmFyZSBjb21tb2RvIG1vbGVzdGllIHNpdCBhbWV0LCBjb25zZXF1YXQgdmVsIGVz +dC4gSW4gaGFjIGhhYml0YXNzZSBwbGF0ZWEgZGljdHVtc3QuIENyYXMgcmlzdXMg +cXVhbSwgc3VzY2lwaXQgc2VtcGVyIGltcGVyZGlldCB1bHRyaWNlcywgdHJpc3Rp +cXVlIG5lYyBzYXBpZW4uIFBlbGxlbnRlc3F1ZSBjb25kaW1lbnR1bSwgdHVycGlz +IG5lYyBzb2RhbGVzIGNvbnNlY3RldHVyLCBkaWFtIHNlbSBiaWJlbmR1bSBkaWFt +LCB2aXRhZSBhbGlxdWV0IG1pIGFyY3UgYSBhcmN1LiBQaGFzZWxsdXMgaGVuZHJl +cml0LCBtYWduYSBxdWlzIGdyYXZpZGEgZmVybWVudHVtLCBtYWduYSBsZWN0dXMg +c2VtcGVyIG1hc3NhLCBzZWQgY29uZ3VlIHVybmEgbmliaCBhdCBudWxsYS4gQ3Jh +cyBzZWQgbmliaCBtaS4gTnVsbGEgdGVtcG9yIHByZXRpdW0gcG9ydGEuCgpVdCBp +ZCBqdXN0byB2aXRhZSB0dXJwaXMgY29udmFsbGlzIGNvbmd1ZS4gQ3VyYWJpdHVy +IG1hbGVzdWFkYSBwdXJ1cyBlZ2V0IGRpYW0gYXVjdG9yIGV0IHVsbGFtY29ycGVy +IHZlbGl0IG1hbGVzdWFkYS4gUGhhc2VsbHVzIHNjZWxlcmlzcXVlIGZlcm1lbnR1 +bSBsaWJlcm8uIE1vcmJpIGR1aSB0b3J0b3IsIGJsYW5kaXQgbWFsZXN1YWRhIGxv +Ym9ydGlzIHVsdHJpY2llcywgYmxhbmRpdCBldCBuaXNpLiBOdWxsYSBtYXR0aXMg +ZmFjaWxpc2lzIGxhY3VzLiBBbGlxdWFtIHNvZGFsZXMgZWxpdCBzaXQgYW1ldCBt +ZXR1cyB2ZW5lbmF0aXMgYSBjb21tb2RvIHF1YW0gYmliZW5kdW0uIFV0IG5vbiB0 +ZWxsdXMgdG9ydG9yLiBTZWQgc2VtcGVyLCBuaXNpIGluIHRlbXBvciB2ZXN0aWJ1 +bHVtLCBzZW0gbnVsbGEgZXVpc21vZCBhcmN1LCB1dCBzb2RhbGVzIGVzdCBudW5j +IGF0IHRlbGx1cy4gRG9uZWMgbm9uIG1ldHVzIG5vbiBkb2xvciBzY2VsZXJpc3F1 +ZSB2aXZlcnJhLiBQaGFzZWxsdXMgbmVjIG51bGxhIGp1c3RvLCB1dCBwb3J0dGl0 +b3IgbnVsbGEuIEZ1c2NlIGZldWdpYXQgZ3JhdmlkYSBzb2xsaWNpdHVkaW4uIFNl +ZCBpZCBoZW5kcmVyaXQgbWF1cmlzLiBEb25lYyBsZWN0dXMgdG9ydG9yLCBwb3J0 +YSBzaXQgYW1ldCBjb25kaW1lbnR1bSBzZWQsIHZhcml1cyBhIHZlbGl0LiBJbiBk +aWN0dW0gZG9sb3Igc2VkIGVzdCBkaWN0dW0gaW4gcnV0cnVtIG5lcXVlIHN1c2Np +cGl0LgoKTWFlY2VuYXMgaW1wZXJkaWV0IGNvbnZhbGxpcyB1bHRyaWNpZXMuIEN1 +bSBzb2NpaXMgbmF0b3F1ZSBwZW5hdGlidXMgZXQgbWFnbmlzIGRpcyBwYXJ0dXJp +ZW50IG1vbnRlcywgbmFzY2V0dXIgcmlkaWN1bHVzIG11cy4gUGVsbGVudGVzcXVl +IG51bmMgdHVycGlzLCBpYWN1bGlzIHF1aXMgZWxlaWZlbmQgZXUsIHRpbmNpZHVu +dCB1dCBsYWN1cy4gQ3VyYWJpdHVyIHZpdGFlIHRlbGx1cyBuaWJoLiBQZWxsZW50 +ZXNxdWUgZWdldCBjb21tb2RvIG5pYmguIE51bGxhIGxhb3JlZXQsIGVyb3MgaWQg +aW1wZXJkaWV0IG1vbGxpcywgbmlzaSBlc3QgZWdlc3RhcyBmZWxpcywgZWdldCBs +dWN0dXMgZW5pbSBsYWN1cyB2aXRhZSBhcmN1LiBOdWxsYW0gaXBzdW0gZGlhbSwg +ZmF1Y2lidXMgZmV1Z2lhdCBjb21tb2RvIGV0LCB0ZW1wb3IgYWMgbGFjdXMuIERv +bmVjIHZlbCBlcmF0IG1hc3NhLCBhdCBwcmV0aXVtIGVzdC4gUGVsbGVudGVzcXVl +IGx1Y3R1cyBuaXNpIG51bGxhLCBhYyBhbGlxdWV0IGF1Z3VlLiBNYWVjZW5hcyBp +ZCBwaGFyZXRyYSBkb2xvci4gUHJvaW4gZGFwaWJ1cyBtYXR0aXMgY3Vyc3VzLiBN +b3JiaSBpbnRlcmR1bSwgZW5pbSBxdWlzIGZlcm1lbnR1bSBwbGFjZXJhdCwgbmlz +aSBtaSBjb25ndWUgc2VtLCBhIHRlbXBvciBuaXNsIG1hdXJpcyBpbiBzZW0uIElu +IGNvbmRpbWVudHVtLCBpcHN1bSBuZWMgdmVuZW5hdGlzIHZlbmVuYXRpcywgbnVs +bGEgZW5pbSBsYW9yZWV0IGFudGUsIG5lYyBoZW5kcmVyaXQgaXBzdW0gbGVjdHVz +IGVnZXQgcmlzdXMuIFBlbGxlbnRlc3F1ZSBlbmltIGxpYmVybywgdGluY2lkdW50 +IHZpdGFlIG1vbGVzdGllIHNpdCBhbWV0LCBncmF2aWRhIG5lYyBsYWN1cy4gRHVp +cyBhYyBzYXBpZW4gbGliZXJvLCBpbiBhbGlxdWV0IHF1YW0uIE1vcmJpIHRlbXBv +ciBsaWJlcm8gcXVpcyBlc3QgZ3JhdmlkYSBzZWQgZWdlc3RhcyBudWxsYSB2dWxw +dXRhdGUuCgpEb25lYyBsYWNpbmlhIG1pIGF0IHRlbGx1cyB2aXZlcnJhIGxvYm9y +dGlzLiBOdWxsYSBpbnRlcmR1bSwgdXJuYSB1dCBmYXVjaWJ1cyBzZW1wZXIsIGR1 +aSBsZWN0dXMgZnJpbmdpbGxhIGVyYXQsIHV0IGFsaXF1YW0gbmlzbCBpcHN1bSBh +dCBlcm9zLiBWaXZhbXVzIGNvbnZhbGxpcyBoZW5kcmVyaXQgYXJjdSBxdWlzIGNv +bmRpbWVudHVtLiBQcmFlc2VudCBhdCBmZXVnaWF0IHNhcGllbi4gUXVpc3F1ZSBu +b24gb3JjaSBhcmN1LiBQZWxsZW50ZXNxdWUgZWdldCBtaSBhcmN1LCBzaXQgYW1l +dCBsdWN0dXMgbnVuYy4gQ3VyYWJpdHVyIGxlY3R1cyBsZW8sIGNvbnNlcXVhdCBz +ZW1wZXIgaW1wZXJkaWV0IGV0LCBlbGVtZW50dW0gdml0YWUgZGlhbS4gUHJvaW4g +cHVydXMgbnVsbGEsIHB1bHZpbmFyIGlkIGltcGVyZGlldCBzZWQsIGZldWdpYXQg +YSBudWxsYS4gUHJvaW4gc3VzY2lwaXQgZWxpdCB2ZWwgYXJjdSB2ZW5lbmF0aXMg +c29kYWxlcy4gQ3JhcyBhY2N1bXNhbiBtYXNzYSBhYyBudWxsYSBwdWx2aW5hciBm +ZXVnaWF0IGV1IGF0IHRlbGx1cy4gUXVpc3F1ZSB2aXRhZSBlcmF0IG9yY2ksIG5v +biB0ZW1wb3IgZW5pbS4gRG9uZWMgb3JuYXJlIGxvYm9ydGlzIG1pIHZpdGFlIGNv +bnNlcXVhdC4gSW50ZWdlciB2aXZlcnJhLCB2ZWxpdCB2ZWwgcGVsbGVudGVzcXVl +IGFjY3Vtc2FuLCBhdWd1ZSBsaWd1bGEgYmxhbmRpdCBuaXNsLCBlZ2V0IGNvbmRp +bWVudHVtIG5lcXVlIGFyY3UgaW4gZXJhdC4gTmFtIHF1aXMgbG9yZW0gbG9yZW0s +IHZlbCBjdXJzdXMgcmlzdXMuIEluIG5lYyBudW5jIGRvbG9yLCBxdWlzIHBvcnR0 +aXRvciBuZXF1ZS4gTWFlY2VuYXMgdHVycGlzIG9kaW8sIGRpY3R1bSB2aXRhZSBj +b21tb2RvIHRpbmNpZHVudCwgYWxpcXVhbSB1dCBhdWd1ZS4gUGhhc2VsbHVzIGF0 +IGp1c3RvIGxhY3VzLiBQaGFzZWxsdXMgc2l0IGFtZXQgdXJuYSBhdCBhbnRlIHZh +cml1cyBwZWxsZW50ZXNxdWUuIFBlbGxlbnRlc3F1ZSBpbiBsaWJlcm8gYWMgdXJu +YSBmcmluZ2lsbGEgdGluY2lkdW50IHZpdGFlIHNlZCB2ZWxpdC4KCkRvbmVjIGFj +IHJpc3VzIHNhcGllbi4gTnVsbGEgZmFjaWxpc2kuIERvbmVjIGF0IHZlbGl0IG5v +biBudW5jIHRlbXB1cyBmZXJtZW50dW0uIEN1bSBzb2NpaXMgbmF0b3F1ZSBwZW5h +dGlidXMgZXQgbWFnbmlzIGRpcyBwYXJ0dXJpZW50IG1vbnRlcywgbmFzY2V0dXIg +cmlkaWN1bHVzIG11cy4gRnVzY2UgaW50ZXJkdW0gYWxpcXVhbSBsb3JlbSwgaW4g +c2NlbGVyaXNxdWUgZW5pbSB0aW5jaWR1bnQgaWQuIFByYWVzZW50IHNvbGxpY2l0 +dWRpbiBkdWkgbmVjIGxhY3VzIGxhY2luaWEgcGxhY2VyYXQuIFV0IGVnZXQgbWF0 +dGlzIG1ldHVzLiBJbiB2ZWwgc2VtIHVybmEsIGV1IGNvbnZhbGxpcyB0dXJwaXMu +IER1aXMgaGVuZHJlcml0IGlwc3VtIHVybmEsIG5vbiBwZWxsZW50ZXNxdWUgb3Jj +aS4gVXQgZXN0IG1ldHVzLCBlbGVtZW50dW0gaW4gYmliZW5kdW0gaWQsIHZpdmVy +cmEgZGlnbmlzc2ltIHRlbGx1cy4gRHVpcyBpZCBlcmF0IGxpYmVyby4gVmVzdGli +dWx1bSBpbiBsaWJlcm8gbm9uIHNlbSB0aW5jaWR1bnQgZWxlbWVudHVtIHNlZCBl +dCBuaXNpLiBQaGFzZWxsdXMgbGVjdHVzIGVyYXQsIGVsZWlmZW5kIGlkIHNhZ2l0 +dGlzIHV0LCBmcmluZ2lsbGEgc2VkIHR1cnBpcy4gQWxpcXVhbSB2ZWwgZXJvcyBp +ZCBudW5jIHZlbmVuYXRpcyB1bGxhbWNvcnBlci4gQWxpcXVhbSB0cmlzdGlxdWUg +bWFzc2EgYSBtYXVyaXMgc2NlbGVyaXNxdWUgbm9uIGZhdWNpYnVzIG5lcXVlIHVs +dHJpY2llcy4KCkFsaXF1YW0gY29uc2VxdWF0IGx1Y3R1cyBlbGl0LCBpZCBtYWxl +c3VhZGEgZXJvcyBsb2JvcnRpcyB1dC4gTnVsbGFtIGEgaGVuZHJlcml0IGxpYmVy +by4gTWFlY2VuYXMgZXQgc2VtIGF0IGVyYXQgbWF0dGlzIGVsZWlmZW5kIHNlZCBz +aXQgYW1ldCBzZW0uIE51bGxhIGxvYm9ydGlzIG5pYmggYSBvcmNpIGVsZWlmZW5k +IHRlbXBvci4gUHJvaW4gbnVsbGEgZmVsaXMsIHZlbmVuYXRpcyBxdWlzIHVsdHJp +Y2llcyB2ZWwsIGFsaXF1ZXQgaW4gZG9sb3IuIFBoYXNlbGx1cyB0aW5jaWR1bnQg +ZG9sb3IgZmVybWVudHVtIG1hc3NhIGlhY3VsaXMgZGlnbmlzc2ltIHZlbCB1dCBk +dWkuIFByb2luIHRpbmNpZHVudCBzYWdpdHRpcyBzb2RhbGVzLiBQcm9pbiBtYXR0 +aXMgdmFyaXVzIGVuaW0sIGF0IGx1Y3R1cyBzZW0gaGVuZHJlcml0IGV1LiBVdCB2 +aXRhZSBncmF2aWRhIG9kaW8uIEFsaXF1YW0gZXJhdCB2b2x1dHBhdC4gTnVsbGEg +ZnJpbmdpbGxhIHBsYWNlcmF0IHZlbGl0LCBzZWQgYWxpcXVldCBudWxsYSBpbXBl +cmRpZXQgYS4gRG9uZWMgZWxlaWZlbmQgcHVsdmluYXIgb3JjaSwgaW4gcG9ydGEg +ZGlhbSBwb3N1ZXJlIGJpYmVuZHVtLiBQcm9pbiBlZ2VzdGFzIHBvcnR0aXRvciBh +bGlxdWFtLiBRdWlzcXVlIHNhcGllbiBmZWxpcywgaGVuZHJlcml0IGlkIGNvbmd1 +ZSBldSwgdGluY2lkdW50IHNlZCBlc3QuIFZpdmFtdXMgY3Vyc3VzIHByZXRpdW0g +ZXJhdCB1dCBmYXVjaWJ1cy4gSW50ZWdlciBsaWJlcm8gZWxpdCwgdmVuZW5hdGlz +IHZlbCBhbGlxdWV0IG5lYywgaW50ZXJkdW0gaW4gZW5pbS4gTmFtIGEgdmVsaXQg +anVzdG8sIHZpdGFlIGlhY3VsaXMgdHVycGlzLgoKVml2YW11cyBuaWJoIGxpZ3Vs +YSwgaW50ZXJkdW0gdmVzdGlidWx1bSB2ZW5lbmF0aXMgdml0YWUsIGZlcm1lbnR1 +bSBpbiBtYWduYS4gU3VzcGVuZGlzc2UgZHVpIGFyY3UsIGdyYXZpZGEgYWMgZWdl +c3RhcyBub24sIGNvbmd1ZSBub24gZGlhbS4gUGVsbGVudGVzcXVlIGhhYml0YW50 +IG1vcmJpIHRyaXN0aXF1ZSBzZW5lY3R1cyBldCBuZXR1cyBldCBtYWxlc3VhZGEg +ZmFtZXMgYWMgdHVycGlzIGVnZXN0YXMuIFBlbGxlbnRlc3F1ZSBwdWx2aW5hciBw +aGFyZXRyYSBsb3JlbSwgZXQgcG9zdWVyZSBzZW0gY29uZ3VlIHV0LiBTZWQgZWxl +bWVudHVtIGNvbnZhbGxpcyBkb2xvciBldSB2dWxwdXRhdGUuIEludGVnZXIgbmli +aCBqdXN0bywgcGVsbGVudGVzcXVlIGVnZXQgdWx0cmljaWVzIGF1Y3RvciwgZmVy +bWVudHVtIG5vbiBtaS4gUGVsbGVudGVzcXVlIGF0IG5lcXVlIGVzdC4gQ3VyYWJp +dHVyIHBlbGxlbnRlc3F1ZSBhcmN1IHNlZCBsYWN1cyBwbGFjZXJhdCBxdWlzIGRp +Z25pc3NpbSBvcmNpIHBsYWNlcmF0LiBOdWxsYW0gcGVsbGVudGVzcXVlIGxpYmVy +byBpZCBhbnRlIGRhcGlidXMgc2VkIGJpYmVuZHVtIG1ldHVzIHRyaXN0aXF1ZS4g +Vml2YW11cyBpZCBkdWkgcXVpcyBhcmN1IHVsdHJpY2VzIHZpdmVycmEuIFBlbGxl +bnRlc3F1ZSBhdWN0b3IgbmlzaSBzZWQgZXN0IGxhb3JlZXQgdml2ZXJyYS4gU3Vz +cGVuZGlzc2UgbGFjaW5pYSBsZWN0dXMgbWFzc2EuIEFsaXF1YW0gZXJhdCB2b2x1 +dHBhdC4KClF1aXNxdWUgdWxsYW1jb3JwZXIgYXVndWUgaW4ganVzdG8gdWx0cmlj +aWVzIHBvcnRhLiBTZWQgcXVpcyBhcmN1IGFjIGxvcmVtIGFjY3Vtc2FuIHBvc3Vl +cmUuIE1hdXJpcyBjb25zZXF1YXQsIGxpYmVybyBlZ2V0IGZldWdpYXQgbG9ib3J0 +aXMsIGlwc3VtIGZlbGlzIHZlc3RpYnVsdW0gdGVsbHVzLCB2ZWwgdWx0cmljZXMg +aXBzdW0gYXJjdSBldSBuaXNsLiBDdW0gc29jaWlzIG5hdG9xdWUgcGVuYXRpYnVz +IGV0IG1hZ25pcyBkaXMgcGFydHVyaWVudCBtb250ZXMsIG5hc2NldHVyIHJpZGlj +dWx1cyBtdXMuIEluIGhhYyBoYWJpdGFzc2UgcGxhdGVhIGRpY3R1bXN0LiBEdWlz +IHZpdmVycmEgdHVycGlzIHZlbCBlbGl0IGVsZWlmZW5kIGF0IHBvcnR0aXRvciB2 +ZWxpdCByaG9uY3VzLiBTZWQgYXQgZG9sb3IgcXVpcyBuaXNpIGNvbW1vZG8gcG9y +dHRpdG9yLiBNb3JiaSBsb3JlbSBvcmNpLCBjb21tb2RvIGEgbGFvcmVldCBuZWMs +IHZhcml1cyBhdCBsYWN1cy4gTWF1cmlzIHNlZCB2YXJpdXMgZW5pbS4gQWxpcXVh +bSBncmF2aWRhIGFkaXBpc2Npbmcgc2VtIG5vbiBzZW1wZXIuIFN1c3BlbmRpc3Nl +IGVnZXQgZ3JhdmlkYSBudW5jLgoKQ3JhcyB2ZXN0aWJ1bHVtIHRvcnRvciBuZWMg +bGlndWxhIG9ybmFyZSB0ZW1wb3IuIFV0IGlhY3VsaXMgbGlndWxhIGV0IGxlY3R1 +cyB0aW5jaWR1bnQgaWFjdWxpcy4gTWFlY2VuYXMgcHVsdmluYXIgdm9sdXRwYXQg +bGFjaW5pYS4gQ3VyYWJpdHVyIGhlbmRyZXJpdCBtYWxlc3VhZGEgbGVjdHVzLCBz +ZWQgbWFsZXN1YWRhIGVyb3MgZWdlc3RhcyBlZ2V0LiBBbGlxdWFtIGJpYmVuZHVt +IHZhcml1cyBvZGlvIHZhcml1cyBtYXR0aXMuIFByb2luIGFjIHJob25jdXMgYXJj +dS4gTnVsbGEgc3VzY2lwaXQgdG9ydG9yIGEgZXN0IHZpdmVycmEgdWxsYW1jb3Jw +ZXIuIEN1cmFiaXR1ciBldCBkdWkgZGlhbS4gU2VkIGF0IG5lcXVlIG5pc2wuIEN1 +cmFiaXR1ciBzYWdpdHRpcyBvcmNpIG5pc2wuIEN1bSBzb2NpaXMgbmF0b3F1ZSBw +ZW5hdGlidXMgZXQgbWFnbmlzIGRpcyBwYXJ0dXJpZW50IG1vbnRlcywgbmFzY2V0 +dXIgcmlkaWN1bHVzIG11cy4KCkNyYXMgZmVsaXMgbWV0dXMsIHZhcml1cyBzaXQg +YW1ldCBjb25zZXF1YXQgZWdldCwgc2VtcGVyIHZpdGFlIGxlby4gTWF1cmlzIHV0 +IG5pc2kgbGFjdXMsIGEgcHJldGl1bSBsYWN1cy4gRHVpcyBlZ2V0IGVzdCBuZWMg +ZG9sb3Igc29sbGljaXR1ZGluIGZlcm1lbnR1bS4gTWFlY2VuYXMgb3JuYXJlIGFk +aXBpc2NpbmcgZHVpLCB2aXRhZSBwZWxsZW50ZXNxdWUgbmlzbCBhbGlxdWFtIGEu +IFZpdmFtdXMgYXVjdG9yIGZyaW5naWxsYSBsaWd1bGEsIGlkIHRlbXBvciBqdXN0 +byBjb25ndWUgYS4gUHJhZXNlbnQgcXVpcyBsYW9yZWV0IGF1Z3VlLiBEb25lYyBp +ZCBvcmNpIHV0IG5pc2kgdml2ZXJyYSBjb21tb2RvLiBQZWxsZW50ZXNxdWUgZGlj +dHVtIHZhcml1cyBvcmNpIHZlbCBwaGFyZXRyYS4gTnVuYyBsaWJlcm8gbmlzbCBt +YXNzYSBudW5jLg== + + + + text/plain + + + + + \ No newline at end of file -- cgit v1.2.3