package com.bwssystems.HABridge; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.PosixFilePermission; import java.security.GeneralSecurityException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import org.apache.http.conn.util.InetAddressUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.bwssystems.HABridge.util.BackupHandler; import com.bwssystems.HABridge.util.JsonTransformer; import com.google.gson.Gson; public class BridgeSettings extends BackupHandler { private static final Logger log = LoggerFactory.getLogger(BridgeSettings.class); private BridgeSettingsDescriptor theBridgeSettings; private BridgeControlDescriptor bridgeControl; private BridgeSecurity bridgeSecurity; private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss"); public BridgeSettings() { super(); bridgeControl = new BridgeControlDescriptor(); theBridgeSettings = new BridgeSettingsDescriptor(); bridgeSecurity = null; String theKey = System.getProperty("security.key"); if(theKey == null) theKey = "IWantMyPasswordsToBeAbleToBeDecodedPleaseSeeTheReadme"; String execGarden = System.getProperty("exec.garden"); bridgeSecurity = new BridgeSecurity(theKey.toCharArray(), execGarden); String ipV6Stack = System.getProperty("ipV6Stack"); if(ipV6Stack == null || !ipV6Stack.equalsIgnoreCase("true")) { System.setProperty("java.net.preferIPv4Stack" , "true"); } } public BridgeControlDescriptor getBridgeControl() { return bridgeControl; } public BridgeSettingsDescriptor getBridgeSettingsDescriptor() { return theBridgeSettings; } public BridgeSecurity getBridgeSecurity() { return bridgeSecurity; } public static String getCurrentDate() { return dateFormat.format(new Date()); } public void buildSettings() { String addressString = null; String theVeraAddress = null; String theSomfyAddress = null; String theHarmonyAddress = null; String configFileProperty = System.getProperty("config.file"); if(configFileProperty == null) { Path filePath = Paths.get(Configuration.CONFIG_FILE); if(Files.exists(filePath) && Files.isReadable(filePath)) configFileProperty = Configuration.CONFIG_FILE; } String serverPortOverride = System.getProperty("server.port"); String serverIpOverride = System.getProperty("server.ip"); if(configFileProperty != null) { log.info("reading from config file: " + configFileProperty); theBridgeSettings.setConfigfile(configFileProperty); _loadConfig(); } else { log.info("reading from system properties"); theBridgeSettings.setNumberoflogmessages(Configuration.NUMBER_OF_LOG_MESSAGES); theBridgeSettings.setFarenheit(true); theBridgeSettings.setConfigfile(Configuration.CONFIG_FILE); theBridgeSettings.setServerPort(System.getProperty("server.port", Configuration.DEFAULT_WEB_PORT)); theBridgeSettings.setUpnpConfigAddress(System.getProperty("upnp.config.address")); theBridgeSettings.setUpnpDeviceDb(System.getProperty("upnp.device.db")); theBridgeSettings.setUpnpResponsePort(System.getProperty("upnp.response.port", Configuration.UPNP_RESPONSE_PORT)); theVeraAddress = System.getProperty("vera.address"); IpList theVeraList = null; if(theVeraAddress != null) { try { theVeraList = new Gson().fromJson(theVeraAddress, IpList.class); } catch (Exception e) { try { theVeraList = new Gson().fromJson("{devices:[{name:default,ip:" + theVeraAddress + "}]}", IpList.class); } catch (Exception et) { log.error("Cannot parse vera.address, not set with message: " + e.getMessage(), e); theVeraList = null; } } } theBridgeSettings.setVeraAddress(theVeraList); theHarmonyAddress = System.getProperty("harmony.address"); IpList theHarmonyList = null; if(theHarmonyAddress != null) { try { theHarmonyList = new Gson().fromJson(theHarmonyAddress, IpList.class); } catch (Exception e) { try { theHarmonyList = new Gson().fromJson("{devices:[{name:default,ip:" + theHarmonyAddress + "}]}", IpList.class); } catch (Exception et) { log.error("Cannot parse harmony.address, not set with message: " + e.getMessage(), e); theHarmonyList = null; } } } theBridgeSettings.setHarmonyAddress(theHarmonyList); theSomfyAddress = System.getProperty("somfy.address"); IpList theSomfyList = null; if(theSomfyAddress != null) { try { theSomfyList = new Gson().fromJson(theSomfyAddress, IpList.class); } catch (Exception e) { try { theSomfyList = new Gson().fromJson("{devices:[{name:default,ip:" + theSomfyAddress + "}]}", IpList.class); } catch (Exception et) { log.error("Cannot parse somfy.address, not set with message: " + e.getMessage(), e); theSomfyList = null; } } } theBridgeSettings.setSomfyAddress(theSomfyList); theBridgeSettings.setUpnpStrict(Boolean.parseBoolean(System.getProperty("upnp.strict", "true"))); theBridgeSettings.setTraceupnp(Boolean.parseBoolean(System.getProperty("trace.upnp", "false"))); theBridgeSettings.setButtonsleep(Integer.parseInt(System.getProperty("button.sleep", Configuration.DEFAULT_BUTTON_SLEEP))); theBridgeSettings.setNestuser(System.getProperty("nest.user")); theBridgeSettings.setNestpwd(System.getProperty("nest.pwd")); } if(theBridgeSettings.getUpnpConfigAddress() == null || theBridgeSettings.getUpnpConfigAddress().trim().equals("") || theBridgeSettings.getUpnpConfigAddress().trim().equals("0.0.0.0")) { addressString = checkIpAddress(null, true); if(addressString != null) { theBridgeSettings.setUpnpConfigAddress(addressString); log.info("Adding " + addressString + " as our default upnp config address."); } else log.error("Cannot get ip address of this host."); } else { addressString = checkIpAddress(theBridgeSettings.getUpnpConfigAddress(), false); if(addressString == null) log.warn("The upnp config address, " + theBridgeSettings.getUpnpConfigAddress() + ", does not match any known IP's on this host."); } if(theBridgeSettings.getUpnpResponsePort() == null) theBridgeSettings.setUpnpResponsePort(Configuration.UPNP_RESPONSE_PORT); if(theBridgeSettings.getServerPort() == null) theBridgeSettings.setServerPort(Configuration.DEFAULT_WEB_PORT); if(theBridgeSettings.getUpnpDeviceDb() == null) theBridgeSettings.setUpnpDeviceDb(Configuration.DEVICE_DB_DIRECTORY); if(theBridgeSettings.getNumberoflogmessages() == null || theBridgeSettings.getNumberoflogmessages() <= 0) theBridgeSettings.setNumberoflogmessages(new Integer(Configuration.NUMBER_OF_LOG_MESSAGES)); if(theBridgeSettings.getButtonsleep() == null || theBridgeSettings.getButtonsleep() < 0) theBridgeSettings.setButtonsleep(Integer.parseInt(Configuration.DEFAULT_BUTTON_SLEEP)); theBridgeSettings.setVeraconfigured(theBridgeSettings.isValidVera()); theBridgeSettings.setHarmonyconfigured(theBridgeSettings.isValidHarmony()); theBridgeSettings.setNestConfigured(theBridgeSettings.isValidNest()); theBridgeSettings.setHueconfigured(theBridgeSettings.isValidHue()); theBridgeSettings.setHalconfigured(theBridgeSettings.isValidHal()); theBridgeSettings.setMqttconfigured(theBridgeSettings.isValidMQTT()); theBridgeSettings.setHassconfigured(theBridgeSettings.isValidHass()); theBridgeSettings.setDomoticzconfigured(theBridgeSettings.isValidDomoticz()); theBridgeSettings.setSomfyconfigured(theBridgeSettings.isValidSomfy()); // Lifx is either configured or not, so it does not need an update. if(serverPortOverride != null) theBridgeSettings.setServerPort(serverPortOverride); if(serverIpOverride != null) theBridgeSettings.setWebaddress(serverIpOverride); setupParams(Paths.get(theBridgeSettings.getConfigfile()), ".cfgbk", "habridge.config-"); bridgeSecurity.setSecurityData(theBridgeSettings.getSecurityData()); if(theBridgeSettings.getWhitelist() != null) { bridgeSecurity.convertWhitelist(theBridgeSettings.getWhitelist()); theBridgeSettings.removeWhitelist(); updateConfigFile(); } } public void loadConfig() { if(theBridgeSettings.getConfigfile() != null) _loadConfig(); } private void _loadConfig() { Path configPath = Paths.get(theBridgeSettings.getConfigfile()); _loadConfig(configPath); } private void _loadConfig(Path aPath) { String jsonContent = configReader(aPath); if(jsonContent == null) return; try { theBridgeSettings = new Gson().fromJson(jsonContent, BridgeSettingsDescriptor.class); } catch (Exception e) { log.warn("Issue loading values from file: " + aPath.toUri().toString() + ", Gson convert failed."); theBridgeSettings = new BridgeSettingsDescriptor(); theBridgeSettings.setConfigfile(aPath.toString()); } } public void save(BridgeSettingsDescriptor newBridgeSettings) { log.debug("Save HA Bridge settings."); Path configPath = Paths.get(theBridgeSettings.getConfigfile()); JsonTransformer aRenderer = new JsonTransformer(); if(bridgeSecurity.isSettingsChanged()) { try { newBridgeSettings.setSecurityData(bridgeSecurity.getSecurityDescriptorData()); } catch (UnsupportedEncodingException e) { log.warn("could not get encoded security data: " + e.getMessage()); return; } catch (GeneralSecurityException e) { log.warn("could not get encoded security data: " + e.getMessage()); return; } bridgeSecurity.setSettingsChanged(false); } String jsonValue = aRenderer.render(newBridgeSettings); configWriter(jsonValue, configPath); _loadConfig(configPath); } public void updateConfigFile() { log.debug("Save HA Bridge settings."); Path configPath = Paths.get(theBridgeSettings.getConfigfile()); JsonTransformer aRenderer = new JsonTransformer(); if(bridgeSecurity.isSettingsChanged()) { try { theBridgeSettings.setSecurityData(bridgeSecurity.getSecurityDescriptorData()); } catch (UnsupportedEncodingException e) { log.warn("could not get encoded security data: " + e.getMessage()); return; } catch (GeneralSecurityException e) { log.warn("could not get encoded security data: " + e.getMessage()); return; } bridgeSecurity.setSettingsChanged(false); } String jsonValue = aRenderer.render(theBridgeSettings); configWriter(jsonValue, configPath); _loadConfig(configPath); } private synchronized void configWriter(String content, Path filePath) { if(Files.exists(filePath) && !Files.isWritable(filePath)){ log.error("Error file is not writable: " + filePath); return; } if(Files.notExists(filePath.getParent())) { try { Files.createDirectories(filePath.getParent()); } catch (IOException e) { log.error("Error creating the directory: " + filePath + " message: " + e.getMessage(), e); } } try { Path target = null; if(Files.exists(filePath)) { target = FileSystems.getDefault().getPath(filePath.getParent().toString(), "habridge.config.old." + getCurrentDate()); Files.move(filePath, target); } Files.write(filePath, content.getBytes(), StandardOpenOption.CREATE); // set attributes to be for user only // using PosixFilePermission to set file permissions Set<PosixFilePermission> perms = new HashSet<PosixFilePermission>(); // add owners permission perms.add(PosixFilePermission.OWNER_READ); perms.add(PosixFilePermission.OWNER_WRITE); try { String osName = System.getProperty("os.name"); if(osName.toLowerCase().indexOf("win") < 0) Files.setPosixFilePermissions(filePath, perms); } catch(UnsupportedOperationException e) { log.info("Cannot set permissions for config file on this system as it is not supported. Continuing"); } if(target != null) Files.delete(target); } catch (IOException e) { log.error("Error writing the file: " + filePath + " message: " + e.getMessage(), e); } } private String configReader(Path filePath) { String content = null; if(Files.notExists(filePath) || !Files.isReadable(filePath)){ log.warn("Error reading the file: " + filePath + " - Does not exist or is not readable. continuing..."); return null; } try { content = new String(Files.readAllBytes(filePath)); } catch (IOException e) { log.error("Error reading the file: " + filePath + " message: " + e.getMessage(), e); } return content; } private String checkIpAddress(String ipAddress, boolean checkForLocalhost) { Enumeration<NetworkInterface> ifs = null; try { ifs = NetworkInterface.getNetworkInterfaces(); } catch(SocketException e) { log.error("checkIpAddress cannot get ip address of this host, Exiting with message: " + e.getMessage(), e); return null; } String addressString = null; InetAddress address = null; while (ifs.hasMoreElements() && addressString == null) { NetworkInterface xface = ifs.nextElement(); Enumeration<InetAddress> addrs = xface.getInetAddresses(); String name = xface.getName(); int IPsPerNic = 0; while (addrs.hasMoreElements() && IPsPerNic == 0) { address = addrs.nextElement(); if (InetAddressUtils.isIPv4Address(address.getHostAddress())) { log.debug(name + " ... has IPV4 addr " + address); if(checkForLocalhost && (!name.equalsIgnoreCase(Configuration.LOOP_BACK_INTERFACE) || !address.getHostAddress().equalsIgnoreCase(Configuration.LOOP_BACK_ADDRESS))) { IPsPerNic++; addressString = address.getHostAddress(); log.debug("checkIpAddress found " + addressString + " from interface " + name); } else if(ipAddress != null && ipAddress.equalsIgnoreCase(address.getHostAddress())){ addressString = ipAddress; IPsPerNic++; } } } } return addressString; } }