diff --git a/README.md b/README.md
index 17afc4ac..e111c853 100644
--- a/README.md
+++ b/README.md
@@ -12,4 +12,4 @@ The branching model (not fully in place yet) is as follows*:
\* - this has been heavily influenced by http://nvie.com/posts/a-successful-git-branching-model/.
-The eclipse_paho_contribution branch is what was submitted to the Eclipse Paho project in https://bugs.eclipse.org/bugs/show_bug.cgi?id=458899.
+The eclipse_paho tags is what was submitted to the Eclipse Paho project in https://bugs.eclipse.org/bugs/show_bug.cgi?id=458899.
diff --git a/mqtt-spy-common/.classpath b/mqtt-spy-common/.classpath
index e9e64704..a9b46af7 100644
--- a/mqtt-spy-common/.classpath
+++ b/mqtt-spy-common/.classpath
@@ -6,6 +6,7 @@
+
diff --git a/mqtt-spy-common/pom.xml b/mqtt-spy-common/pom.xml
index 0a1c4139..5554f539 100644
--- a/mqtt-spy-common/pom.xml
+++ b/mqtt-spy-common/pom.xml
@@ -3,7 +3,7 @@
pl.baczkowicz.mqttspy
mqtt-spy-common
- 0.0.9
+ 0.0.10
UTF-8
@@ -40,7 +40,13 @@
commons-codec
1.9
-
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+ 1.52
+
+
diff --git a/mqtt-spy/buildNumber.properties b/mqtt-spy/buildNumber.properties
index 9a70aaa0..658b4c4b 100644
--- a/mqtt-spy/buildNumber.properties
+++ b/mqtt-spy/buildNumber.properties
@@ -1,3 +1,3 @@
#maven.buildNumber.plugin properties file
-#Sun May 31 21:03:09 BST 2015
-buildNumber=73
+#Sun May 31 21:46:28 BST 2015
+buildNumber=74
diff --git a/mqtt-spy/pom.xml b/mqtt-spy/pom.xml
index 6ee31d43..e0bcf0c5 100644
--- a/mqtt-spy/pom.xml
+++ b/mqtt-spy/pom.xml
@@ -4,7 +4,7 @@
pl.baczkowicz.mqttspy
mqtt-spy
- 0.1.10
+ 0.1.11-beta
jar
mqtt-spy
@@ -38,7 +38,7 @@
pl.baczkowicz.mqttspy
mqtt-spy-common
- 0.0.9
+ 0.0.10
maven-jaxb2-plugin
diff --git a/mqtt-spy/src/main/java/pl/baczkowicz/mqttspy/connectivity/MqttAsyncConnection.java b/mqtt-spy/src/main/java/pl/baczkowicz/mqttspy/connectivity/MqttAsyncConnection.java
index 8c64455e..5751d94b 100644
--- a/mqtt-spy/src/main/java/pl/baczkowicz/mqttspy/connectivity/MqttAsyncConnection.java
+++ b/mqtt-spy/src/main/java/pl/baczkowicz/mqttspy/connectivity/MqttAsyncConnection.java
@@ -182,10 +182,10 @@ public boolean publish(final String publicationTopic, final byte[] data, final i
{
try
{
- logger.info("Publishing message on topic \"" + publicationTopic + "\". Payload = \"" + data + "\"");
+ logger.info("Publishing message on topic \"" + publicationTopic + "\". Payload size = \"" + data.length + "\"");
client.publish(publicationTopic, data, qos, retained);
- logger.trace("Published message on topic \"" + publicationTopic + "\". Payload = \"" + data + "\"");
+ logger.trace("Published message on topic \"" + publicationTopic + "\". Payload size = \"" + data.length + "\"");
statisticsManager.messagePublished(getId(), publicationTopic);
return true;
diff --git a/mqtt-spy/src/main/java/pl/baczkowicz/mqttspy/ui/controllers/edit/EditConnectionConnectivityController.java b/mqtt-spy/src/main/java/pl/baczkowicz/mqttspy/ui/controllers/edit/EditConnectionConnectivityController.java
index 09d9fecd..1f017915 100644
--- a/mqtt-spy/src/main/java/pl/baczkowicz/mqttspy/ui/controllers/edit/EditConnectionConnectivityController.java
+++ b/mqtt-spy/src/main/java/pl/baczkowicz/mqttspy/ui/controllers/edit/EditConnectionConnectivityController.java
@@ -106,7 +106,7 @@ public void initialize(URL location, ResourceBundle resources)
{
@Override
public void changed(ObservableValue observable, Object oldValue, Object newValue)
- {
+ {
parent.updateConnectionName();
onChange();
@@ -216,7 +216,8 @@ public UserInterfaceMqttConnectionDetails readValues(final UserInterfaceMqttConn
for (final String serverURI : serverURIs)
{
logger.trace("Adding " + serverURI);
- connection.getServerURI().add(serverURI.trim());
+ // Trim and remove any prefixes - these are done dynamically based on SSL mode
+ connection.getServerURI().add(serverURI.trim().replaceAll(MqttUtils.TCP_PREFIX, "").replaceAll(MqttUtils.SSL_PREFIX, ""));
}
if (brokerAddressText.getText().endsWith(ConnectionUtils.SERVER_DELIMITER))
{
diff --git a/mqtt-spy/src/main/java/pl/baczkowicz/mqttspy/ui/controllers/edit/EditConnectionSecurityController.java b/mqtt-spy/src/main/java/pl/baczkowicz/mqttspy/ui/controllers/edit/EditConnectionSecurityController.java
index 256b16cf..362fe672 100644
--- a/mqtt-spy/src/main/java/pl/baczkowicz/mqttspy/ui/controllers/edit/EditConnectionSecurityController.java
+++ b/mqtt-spy/src/main/java/pl/baczkowicz/mqttspy/ui/controllers/edit/EditConnectionSecurityController.java
@@ -15,22 +15,49 @@
package pl.baczkowicz.mqttspy.ui.controllers.edit;
import java.net.URL;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import java.util.ResourceBundle;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
+import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
+import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
+import javafx.scene.control.ComboBox;
+import javafx.scene.control.Label;
+import javafx.scene.control.ListCell;
+import javafx.scene.control.ListView;
import javafx.scene.control.PasswordField;
import javafx.scene.control.RadioButton;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableColumn.CellEditEvent;
+import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.AnchorPane;
+import javafx.util.Callback;
+import javafx.util.StringConverter;
+
+import javax.net.ssl.SSLContext;
+
+import pl.baczkowicz.mqttspy.common.generated.SslModeEnum;
+import pl.baczkowicz.mqttspy.common.generated.SslProperty;
+import pl.baczkowicz.mqttspy.common.generated.SslSettings;
import pl.baczkowicz.mqttspy.common.generated.UserCredentials;
import pl.baczkowicz.mqttspy.configuration.ConfiguredConnectionDetails;
import pl.baczkowicz.mqttspy.configuration.generated.UserAuthenticationOptions;
import pl.baczkowicz.mqttspy.configuration.generated.UserInterfaceMqttConnectionDetails;
import pl.baczkowicz.mqttspy.ui.EditConnectionController;
+import pl.baczkowicz.mqttspy.ui.properties.KeyValueProperty;
import pl.baczkowicz.mqttspy.utils.MqttUtils;
/**
@@ -65,6 +92,51 @@ public class EditConnectionSecurityController extends AnchorPane implements Init
@FXML
private PasswordField password;
+ @FXML
+ private ComboBox modeCombo;
+
+ @FXML
+ private ComboBox protocolCombo;
+
+ @FXML
+ private AnchorPane customSocketFactoryPane;
+
+ @FXML
+ private AnchorPane propertiesPane;
+
+ @FXML
+ private TextField certificateAuthorityFile;
+
+ @FXML
+ private TextField clientPassword;
+
+ @FXML
+ private TextField clientKeyFile;
+
+ @FXML
+ private TextField clientAuthorityFile;
+
+ @FXML
+ private Label clientKeyPasswordLabel;
+
+ @FXML
+ private Label clientKeyFileLabel;
+
+ @FXML
+ private Label clientAuthorityFileLabel;
+
+ @FXML
+ private TableView sslPropertiesTable;
+
+ @FXML
+ private TableColumn propertyNameColumn;
+
+ @FXML
+ private TableColumn propertyValueColumn;
+
+ @FXML
+ private Button removePropertyButton;
+
// Other fields
private final ChangeListener basicOnChangeListener = new ChangeListener()
@@ -81,8 +153,8 @@ public void changed(ObservableValue observable, Object oldValue, Object newValue
// ===============================
public void initialize(URL location, ResourceBundle resources)
- {
- // Security
+ {
+ // Authentication
userAuthentication.selectedProperty().addListener(new ChangeListener()
{
@Override
@@ -99,6 +171,130 @@ public void changed(ObservableValue observable, Object oldValue, Object newValue
askForPassword.selectedProperty().addListener(basicOnChangeListener);
predefinedUsername.selectedProperty().addListener(basicOnChangeListener);
predefinedPassword.selectedProperty().addListener(basicOnChangeListener);
+
+ // SSL
+ certificateAuthorityFile.textProperty().addListener(basicOnChangeListener);
+ clientAuthorityFile.textProperty().addListener(basicOnChangeListener);
+ clientKeyFile.textProperty().addListener(basicOnChangeListener);
+ clientPassword.textProperty().addListener(basicOnChangeListener);
+ protocolCombo.getSelectionModel().selectedIndexProperty().addListener(basicOnChangeListener);
+
+ propertyNameColumn.setCellValueFactory(new PropertyValueFactory("key"));
+ propertyNameColumn.setCellFactory(TextFieldTableCell.forTableColumn());
+ propertyNameColumn.setOnEditCommit(new EventHandler>()
+ {
+ @Override
+ public void handle(CellEditEvent event)
+ {
+ KeyValueProperty p = event.getRowValue();
+ String newValue = event.getNewValue();
+ p.keyProperty().set(newValue);
+ onChange();
+ }
+ });
+ propertyValueColumn.setCellValueFactory(new PropertyValueFactory("value"));
+ propertyValueColumn.setCellFactory(TextFieldTableCell.forTableColumn());
+ propertyValueColumn.setOnEditCommit(new EventHandler>()
+ {
+ @Override
+ public void handle(CellEditEvent event)
+ {
+ KeyValueProperty p = event.getRowValue();
+ String newValue = event.getNewValue();
+ p.valueProperty().set(newValue);
+ onChange();
+ }
+ });
+
+ final Map modeEnumText = new HashMap<>();
+ modeEnumText.put(SslModeEnum.DISABLED, "Disabled");
+ modeEnumText.put(SslModeEnum.PROPERTIES, "SSL/TLS properties (using default socket factory)");
+ modeEnumText.put(SslModeEnum.SERVER_ONLY, "Server authentication only (using custom socket factory)");
+ modeEnumText.put(SslModeEnum.SERVER_AND_CLIENT, "Server and client authentication (using custom socket factory)");
+
+ try
+ {
+ final SSLContext context = SSLContext.getDefault();
+ final String[] values = context.getSupportedSSLParameters().getProtocols();
+ final List filteredValues = new ArrayList<>();
+ filteredValues.addAll(Arrays.asList(values));
+ final Iterator i = filteredValues.iterator();
+ while (i.hasNext())
+ {
+ if (i.next().contains("Hello"))
+ {
+ i.remove();
+ }
+ }
+
+ protocolCombo.getSelectionModel().selectedIndexProperty().addListener(basicOnChangeListener);
+ protocolCombo.getItems().addAll(filteredValues);
+ }
+ catch (NoSuchAlgorithmException e)
+ {
+ e.printStackTrace();
+ }
+
+ // SSL Mode
+ modeCombo.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener()
+ {
+ @Override
+ public void changed(ObservableValue observable, Object oldValue, Object newValue)
+ {
+ updateSSL();
+
+ onChange();
+ }
+ });
+ modeCombo.setCellFactory(new Callback, ListCell>()
+ {
+ @Override
+ public ListCell call(ListView l)
+ {
+ return new ListCell()
+ {
+ @Override
+ protected void updateItem(SslModeEnum item, boolean empty)
+ {
+ super.updateItem(item, empty);
+ if (item == null || empty)
+ {
+ setText(null);
+ }
+ else
+ {
+ setText(modeEnumText.get(item));
+ }
+ }
+ };
+ }
+ });
+ modeCombo.setConverter(new StringConverter()
+ {
+ @Override
+ public String toString(SslModeEnum item)
+ {
+ if (item == null)
+ {
+ return null;
+ }
+ else
+ {
+ return modeEnumText.get(item);
+ }
+ }
+
+ @Override
+ public SslModeEnum fromString(String id)
+ {
+ return null;
+ }
+ });
+
+ for (SslModeEnum modeEnum : SslModeEnum.values())
+ {
+ modeCombo.getItems().add(modeEnum);
+ }
}
public void init()
@@ -111,9 +307,27 @@ public void init()
// ===============================
public void onChange()
- {
+ {
parent.onChange();
}
+
+ public void updateSSL()
+ {
+ final boolean serverAndClient = SslModeEnum.SERVER_AND_CLIENT.equals(modeCombo.getSelectionModel().getSelectedItem());
+
+ propertiesPane.setVisible(SslModeEnum.PROPERTIES.equals(modeCombo.getSelectionModel().getSelectedItem()));
+
+ customSocketFactoryPane.setVisible(serverAndClient
+ || SslModeEnum.SERVER_ONLY.equals(modeCombo.getSelectionModel().getSelectedItem()));
+
+ clientPassword.setVisible(serverAndClient);
+ clientKeyFile.setVisible(serverAndClient);
+ clientAuthorityFile.setVisible(serverAndClient);
+
+ clientKeyPasswordLabel.setVisible(serverAndClient);
+ clientKeyFileLabel.setVisible(serverAndClient);
+ clientAuthorityFileLabel.setVisible(serverAndClient);
+ }
@Override
public UserInterfaceMqttConnectionDetails readValues(final UserInterfaceMqttConnectionDetails connection)
@@ -133,6 +347,33 @@ public UserInterfaceMqttConnectionDetails readValues(final UserInterfaceMqttConn
connection.setUserCredentials(userCredentials);
}
+ if (modeCombo.getSelectionModel().getSelectedItem() == null || SslModeEnum.DISABLED.equals(modeCombo.getSelectionModel().getSelectedItem()))
+ {
+ connection.setSSL(null);
+ }
+ else
+ {
+ final SslSettings sslSettings = new SslSettings();
+ sslSettings.setMode(modeCombo.getSelectionModel().getSelectedItem());
+
+ if (SslModeEnum.PROPERTIES.equals(modeCombo.getSelectionModel().getSelectedItem()))
+ {
+ for (final KeyValueProperty property : sslPropertiesTable.getItems())
+ {
+ sslSettings.getProperty().add(new SslProperty(property.keyProperty().getValue(), property.valueProperty().getValue()));
+ }
+ }
+ else
+ {
+ sslSettings.setCertificateAuthorityFile(certificateAuthorityFile.getText());
+ sslSettings.setClientCertificateFile(clientAuthorityFile.getText());
+ sslSettings.setClientKeyFile(clientKeyFile.getText());
+ sslSettings.setClientKeyPassword(clientPassword.getText());
+ sslSettings.setProtocol(protocolCombo.getSelectionModel().getSelectedItem());
+ }
+ connection.setSSL(sslSettings);
+ }
+
return connection;
}
@@ -203,8 +444,66 @@ public void displayConnectionDetails(final ConfiguredConnectionDetails connectio
askForPassword.setSelected(true);
}
+ if (connection.getSSL() == null)
+ {
+ modeCombo.getSelectionModel().select(SslModeEnum.DISABLED);
+ }
+ else
+ {
+ removePropertyButton.setDisable(true);
+ sslPropertiesTable.getItems().clear();
+ sslPropertiesTable.getSelectionModel().selectedItemProperty().addListener(new ChangeListener()
+ {
+ @Override
+ public void changed(ObservableValue observable, Object oldValue, Object newValue)
+ {
+ removePropertyButton.setDisable(false);
+ }
+ });
+
+ modeCombo.getSelectionModel().select(connection.getSSL().getMode());
+
+ certificateAuthorityFile.setText(connection.getSSL().getCertificateAuthorityFile());
+ clientAuthorityFile.setText(connection.getSSL().getClientCertificateFile());
+ clientKeyFile.setText(connection.getSSL().getClientKeyFile());
+ clientPassword.setText(connection.getSSL().getClientKeyPassword());
+ for (final String item : protocolCombo.getItems())
+ {
+ if (item.equals(connection.getSSL().getProtocol()))
+ {
+ protocolCombo.getSelectionModel().select(item);
+ break;
+ }
+ }
+
+ for (final SslProperty property : connection.getSSL().getProperty())
+ {
+ sslPropertiesTable.getItems().add(new KeyValueProperty(property.getName(), property.getValue()));
+ }
+ }
+
updateUserAuthentication();
+ updateSSL();
}
+
+ @FXML
+ private void addProperty()
+ {
+ final KeyValueProperty item = new KeyValueProperty("sample.property", "sampleValue");
+ sslPropertiesTable.getItems().add(item);
+ onChange();
+ }
+
+ @FXML
+ private void removeProperty()
+ {
+ final KeyValueProperty item = sslPropertiesTable.getSelectionModel().getSelectedItem();
+ if (item != null)
+ {
+ sslPropertiesTable.getItems().remove(item);
+ onChange();
+ }
+ }
// ===============================
// === Setters and getters =======
diff --git a/mqtt-spy/src/main/java/pl/baczkowicz/mqttspy/ui/properties/KeyValueProperty.java b/mqtt-spy/src/main/java/pl/baczkowicz/mqttspy/ui/properties/KeyValueProperty.java
new file mode 100644
index 00000000..b31e025f
--- /dev/null
+++ b/mqtt-spy/src/main/java/pl/baczkowicz/mqttspy/ui/properties/KeyValueProperty.java
@@ -0,0 +1,61 @@
+/***********************************************************************************
+ *
+ * Copyright (c) 2014 Kamil Baczkowicz
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *
+ * Kamil Baczkowicz - initial API and implementation and/or initial documentation
+ *
+ */
+package pl.baczkowicz.mqttspy.ui.properties;
+
+import javafx.beans.property.SimpleStringProperty;
+
+/**
+ * Property for FX controls (e.g. table) containing a key and a value.
+ */
+public class KeyValueProperty
+{
+ /** Key as string property. */
+ private SimpleStringProperty key;
+
+ /** Value as string property. */
+ private SimpleStringProperty value;
+
+ /**
+ * Creates the KeyValueProperty object with the provided key and value.
+ *
+ * @param key The key to set
+ * @param value The value to set
+ */
+ public KeyValueProperty(final String key, final String value)
+ {
+ this.key = new SimpleStringProperty(key);
+ this.value = new SimpleStringProperty(value);
+ }
+
+ /**
+ * The key property.
+ *
+ * @return The key property as SimpleStringProperty
+ */
+ public SimpleStringProperty keyProperty()
+ {
+ return this.key;
+ }
+
+ /**
+ * The value property.
+ *
+ * @return The value property as SimpleStringProperty
+ */
+ public SimpleStringProperty valueProperty()
+ {
+ return this.value;
+ }
+}
diff --git a/mqtt-spy/src/main/resources/pl/baczkowicz/mqttspy/ui/fxml/EditConnectionConnectivityPane.fxml b/mqtt-spy/src/main/resources/pl/baczkowicz/mqttspy/ui/fxml/EditConnectionConnectivityPane.fxml
index 09133be8..b0ae0ddd 100644
--- a/mqtt-spy/src/main/resources/pl/baczkowicz/mqttspy/ui/fxml/EditConnectionConnectivityPane.fxml
+++ b/mqtt-spy/src/main/resources/pl/baczkowicz/mqttspy/ui/fxml/EditConnectionConnectivityPane.fxml
@@ -34,7 +34,7 @@
-