/*********************************************************************************** * * 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 * and Eclipse Distribution License v1.0 which accompany this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * * Kamil Baczkowicz - initial API and implementation and/or initial documentation * */ package pl.baczkowicz.mqttspy.connectivity; import java.util.Properties; import org.apache.commons.codec.binary.Base64; import org.eclipse.paho.client.mqttv3.MqttConnectOptions; import pl.baczkowicz.mqttspy.common.generated.MqttConnectionDetails; import pl.baczkowicz.mqttspy.common.generated.ProtocolVersionEnum; import pl.baczkowicz.mqttspy.utils.MqttConfigurationUtils; import pl.baczkowicz.spy.common.generated.Property; import pl.baczkowicz.spy.common.generated.SecureSocketModeEnum; import pl.baczkowicz.spy.exceptions.ConfigurationException; import pl.baczkowicz.spy.exceptions.SpyException; import pl.baczkowicz.spy.security.SecureSocketFactoryBuilder; import pl.baczkowicz.spy.utils.ConversionUtils; /** * Extends JAXB-generated class for storing MQTT connection details, by adding the Paho's MqttConnectOptions. */ public class MqttConnectionDetailsWithOptions extends MqttConnectionDetails { private static final long serialVersionUID = 5693589380291267334L; /** Unique ID for this connection - populated when loading configuration. */ private final String id; /** Paho's MQTT connection options. */ private MqttConnectOptions options; /** * Instantiates the MqttConnectionDetailsWithOptions. * * @param details The configured connection details * * @throws ConfigurationException Thrown when errors detected */ public MqttConnectionDetailsWithOptions(final String id, final MqttConnectionDetails details) throws ConfigurationException { this.id = id; this.setProtocol(details.getProtocol()); // Copy all parameters this.setName(details.getName()); this.setClientID(details.getClientID()); this.getServerURI().addAll(details.getServerURI()); this.setConnectionTimeout(details.getConnectionTimeout()); this.setKeepAliveInterval(details.getKeepAliveInterval()); this.setCleanSession(details.isCleanSession()); this.setLastWillAndTestament(details.getLastWillAndTestament()); this.setUserCredentials(details.getUserCredentials()); this.setReconnectionSettings(details.getReconnectionSettings()); this.setSSL(details.getSSL()); final boolean sslEnabled = details.getSSL() != null && details.getSSL().getMode() != null && !details.getSSL().getMode().equals(SecureSocketModeEnum.DISABLED); this.setWebSocket(details.isWebSocket()); MqttConfigurationUtils.completeServerURIs(this, sslEnabled, Boolean.TRUE.equals(this.isWebSocket())); MqttConfigurationUtils.populateConnectionDefaults(this); try { populateMqttConnectOptions(); } catch (IllegalArgumentException | SpyException e) { throw new ConfigurationException("Invalid configuration parameters", e); } } /** * Populates the Paho's MqttConnectOptions based on the supplied MqttConnectionDetails. * @throws SpyException Thrown when SSL configuration is not valid */ private void populateMqttConnectOptions() throws SpyException { // Populate MQTT options options = new MqttConnectOptions(); if (ProtocolVersionEnum.MQTT_3_1_1.equals(getProtocol())) { options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1); } else if (ProtocolVersionEnum.MQTT_3_1.equals(getProtocol())) { options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1); } else { options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_DEFAULT); } if (getServerURI().size() > 1) { options.setServerURIs(getServerURI().toArray(new String[getServerURI().size()])); } options.setCleanSession(isCleanSession()); options.setConnectionTimeout(getConnectionTimeout()); options.setKeepAliveInterval(getKeepAliveInterval()); if (getUserCredentials() != null) { options.setUserName(getUserCredentials().getUsername()); options.setPassword(ConversionUtils.base64ToString(getUserCredentials().getPassword()).toCharArray()); } if (getLastWillAndTestament() != null) { options.setWill(getLastWillAndTestament().getTopic(), Base64.decodeBase64(getLastWillAndTestament().getValue()), getLastWillAndTestament().getQos(), getLastWillAndTestament().isRetained()); } // SSL and TLS if (getSSL() == null) { // No SSL/TLS settings available } else { if (SecureSocketModeEnum.PROPERTIES.equals(getSSL().getMode())) { Properties props = new Properties(); for (final Property prop : getSSL().getProperty()) { props.put(prop.getName(), prop.getValue()); } options.setSSLProperties(props); } else if (SecureSocketModeEnum.BASIC.equals(getSSL().getMode())) { options.setSocketFactory(SecureSocketFactoryBuilder.getSocketFactory(getSSL().getProtocol())); } else if (SecureSocketModeEnum.SERVER_ONLY.equals(getSSL().getMode())) { options.setSocketFactory(SecureSocketFactoryBuilder.getSocketFactory( getSSL().getProtocol(), getSSL().getCertificateAuthorityFile())); } else if (SecureSocketModeEnum.SERVER_KEYSTORE.equals(getSSL().getMode())) { options.setSocketFactory(SecureSocketFactoryBuilder.getSocketFactory( getSSL().getProtocol(), getSSL().getServerKeyStoreFile(), getSSL().getServerKeyStorePassword())); } else if (SecureSocketModeEnum.SERVER_AND_CLIENT.equals(getSSL().getMode())) { options.setSocketFactory(SecureSocketFactoryBuilder.getSocketFactory( getSSL().getProtocol(), getSSL().getCertificateAuthorityFile(), getSSL().getClientCertificateFile(), getSSL().getClientKeyFile(), getSSL().getClientKeyPassword(), getSSL().isClientKeyPEM())); } else if (SecureSocketModeEnum.SERVER_AND_CLIENT_KEYSTORES.equals(getSSL().getMode())) { options.setSocketFactory(SecureSocketFactoryBuilder.getSocketFactory( getSSL().getProtocol(), getSSL().getServerKeyStoreFile(), getSSL().getServerKeyStorePassword(), getSSL().getClientKeyStoreFile(), getSSL().getClientKeyStorePassword(), getSSL().getClientKeyPassword())); } // TODO: set connection protocol to SSL if not done already } } /** * Gets the MqttConnectOptions. * * @return MqttConnectOptions */ public MqttConnectOptions getOptions() { return options; } public String getId() { return id; } }