001/* 002 * Syncany, www.syncany.org 003 * Copyright (C) 2011-2016 Philipp C. Heckel <philipp.heckel@gmail.com> 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU General Public License as published by 007 * the Free Software Foundation, either version 3 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018package org.syncany.config; 019 020import javax.net.ssl.SSLContext; 021import java.io.File; 022import java.io.FileInputStream; 023import java.io.FileOutputStream; 024import java.security.KeyStore; 025import java.util.Map; 026 027import org.syncany.config.to.UserConfigTO; 028import org.syncany.crypto.CipherException; 029import org.syncany.crypto.CipherUtil; 030import org.syncany.crypto.SaltedSecretKey; 031import org.syncany.util.EnvironmentUtil; 032 033/** 034 * Represents the configuration parameters and application user directory 035 * of the currently logged in user, including system properties that will be 036 * set with every application start. 037 * 038 * @author Philipp C. Heckel (philipp.heckel@gmail.com) 039 */ 040public class UserConfig { 041 /* 042 * Note: 043 * This class can't have any logging methods, because the init() method is called 044 * BEFORE the logging initialization. All errors must be printed to STDERR. 045 */ 046 047 // Daemon-specific config 048 public static final String DAEMON_FILE = "daemon.xml"; 049 public static final String DAEMON_EXAMPLE_FILE = "daemon-example.xml"; 050 public static final String DEFAULT_FOLDER = "Syncany"; 051 public static final String USER_ADMIN = "admin"; 052 public static final String USER_CLI = "CLI"; 053 054 // These fields are not final to enable a PluginOperationTest 055 private static File USER_APP_DIR_WINDOWS = new File(System.getenv("APPDATA") + "\\Syncany"); 056 private static File USER_APP_DIR_UNIX_LIKE = new File(System.getProperty("user.home") + "/.config/syncany"); 057 private static final String USER_LOG_DIR = "logs"; 058 private static final String USER_PLUGINS_LIB_DIR = "plugins/lib"; 059 private static final String USER_PLUGINS_USERDATA_DIR_FORMAT = "plugins/userdata/%s"; 060 private static final String USER_CONFIG_FILE = "userconfig.xml"; 061 private static final String USER_TRUSTSTORE_FILE = "truststore.jks"; 062 private static final String USER_KEYSTORE_FILE = "keystore.jks"; 063 private static final int USER_CONFIG_ENCRYPTION_KEY_LENGTH = 32; 064 065 private static File userConfigDir; 066 private static File userLogDir; 067 private static File userPluginLibDir; 068 private static File userConfigFile; 069 070 private static File userTrustStoreFile; 071 private static KeyStore userTrustStore; 072 073 private static File userKeyStoreFile; 074 private static KeyStore userKeyStore; 075 076 private static boolean preventStandby; 077 private static SaltedSecretKey configEncryptionKey; 078 079 static { 080 init(); 081 } 082 083 public static void init() { 084 if (userConfigDir == null) { 085 initUserAppDirs(); 086 initUserConfig(); 087 initUserTrustStore(); 088 initUserKeyStore(); 089 } 090 } 091 092 public static File getUserConfigDir() { 093 return userConfigDir; 094 } 095 096 public static File getUserLogDir() { 097 return userLogDir; 098 } 099 100 public static File getUserPluginLibDir() { 101 return userPluginLibDir; 102 } 103 104 public static File getUserPluginsUserdataDir(String pluginId) { 105 File pluginConfigDir = new File(userConfigDir, String.format(USER_PLUGINS_USERDATA_DIR_FORMAT, pluginId)); 106 pluginConfigDir.mkdirs(); 107 108 return pluginConfigDir; 109 } 110 111 public static File getUserConfigFile() { 112 return userConfigFile; 113 } 114 115 public static boolean isPreventStandby() { 116 return preventStandby; 117 } 118 119 public static void setPreventStandby(boolean newPreventStandby) { 120 preventStandby = newPreventStandby; 121 } 122 123 public static SaltedSecretKey getConfigEncryptionKey() { 124 return configEncryptionKey; 125 } 126 127 public static KeyStore getUserTrustStore() { 128 // Note: This method might not be used by the main project modules, 129 // but it might be used by plugins. Do not remove unless you are 130 // sure that it is not needed. 131 132 return userTrustStore; 133 } 134 135 public static KeyStore getUserKeyStore() { 136 return userKeyStore; 137 } 138 139 public static void storeTrustStore() { 140 storeKeyStore(userTrustStore, userTrustStoreFile); 141 } 142 143 public static void storeUserKeyStore() { 144 storeKeyStore(userKeyStore, userKeyStoreFile); 145 } 146 147 public static SSLContext createUserSSLContext() throws Exception { 148 return CipherUtil.createSSLContext(userKeyStore, userTrustStore); 149 } 150 151 // General initialization methods 152 153 private static void initUserAppDirs() { 154 userConfigDir = (EnvironmentUtil.isWindows()) ? USER_APP_DIR_WINDOWS : USER_APP_DIR_UNIX_LIKE; 155 userConfigDir.mkdirs(); 156 157 userLogDir = new File(userConfigDir, USER_LOG_DIR); 158 userLogDir.mkdirs(); 159 160 userPluginLibDir = new File(userConfigDir, USER_PLUGINS_LIB_DIR); 161 userPluginLibDir.mkdirs(); 162 } 163 164 private static void initUserConfig() { 165 userConfigFile = new File(userConfigDir, USER_CONFIG_FILE); 166 167 if (userConfigFile.exists()) { 168 loadAndInitUserConfigFile(userConfigFile); 169 } 170 else { 171 writeExampleUserConfigFile(userConfigFile); 172 loadAndInitUserConfigFile(userConfigFile); 173 } 174 } 175 176 private static void loadAndInitUserConfigFile(File userConfigFile) { 177 try { 178 UserConfigTO userConfigTO = UserConfigTO.load(userConfigFile); 179 180 // System properties 181 for (Map.Entry<String, String> systemProperty : userConfigTO.getSystemProperties().entrySet()) { 182 String propertyValue = (systemProperty.getValue() != null) ? systemProperty.getValue() : ""; 183 System.setProperty(systemProperty.getKey(), propertyValue); 184 } 185 186 // Other options 187 preventStandby = userConfigTO.isPreventStandby(); 188 configEncryptionKey = userConfigTO.getConfigEncryptionKey(); 189 } 190 catch (ConfigException e) { 191 System.err.println("ERROR: " + e.getMessage()); 192 System.err.println(" Ignoring user config file!"); 193 System.err.println(); 194 } 195 } 196 197 private static void writeExampleUserConfigFile(File userConfigFile) { 198 UserConfigTO userConfigTO = new UserConfigTO(); 199 200 try { 201 System.out.println("First launch, creating a secret key (could take a sec)..."); 202 SaltedSecretKey configEncryptionKey = CipherUtil.createMasterKey(CipherUtil.createRandomAlphabeticString(USER_CONFIG_ENCRYPTION_KEY_LENGTH)); 203 204 userConfigTO.setConfigEncryptionKey(configEncryptionKey); 205 userConfigTO.save(userConfigFile); 206 } 207 catch (CipherException e) { 208 System.err.println("ERROR: " + e.getMessage()); 209 System.err.println(" Failed to create masterkey."); 210 System.err.println(); 211 } 212 catch (ConfigException e) { 213 System.err.println("ERROR: " + e.getMessage()); 214 System.err.println(" Failed to save to file."); 215 System.err.println(); 216 } 217 } 218 219 // Key store / Trust store methods 220 221 private static void initUserTrustStore() { 222 userTrustStoreFile = new File(userConfigDir, USER_TRUSTSTORE_FILE); 223 userTrustStore = initKeyStore(userTrustStoreFile); 224 } 225 226 private static void initUserKeyStore() { 227 userKeyStoreFile = new File(userConfigDir, USER_KEYSTORE_FILE); 228 userKeyStore = initKeyStore(userKeyStoreFile); 229 } 230 231 private static KeyStore initKeyStore(File keyStoreFile) { 232 try { 233 KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 234 235 if (keyStoreFile.exists()) { 236 FileInputStream trustStoreInputStream = new FileInputStream(keyStoreFile); 237 keyStore.load(trustStoreInputStream, new char[0]); 238 239 trustStoreInputStream.close(); 240 } 241 else { 242 keyStore.load(null, new char[0]); // Initialize empty store 243 } 244 245 return keyStore; 246 } 247 catch (Exception e) { 248 throw new RuntimeException(e); 249 } 250 } 251 252 private static void storeKeyStore(KeyStore keyStore, File keyStoreFile) { 253 try { 254 FileOutputStream trustStoreOutputStream = new FileOutputStream(keyStoreFile); 255 keyStore.store(trustStoreOutputStream, new char[0]); 256 257 trustStoreOutputStream.close(); 258 } 259 catch (Exception e) { 260 throw new RuntimeException("Cannot store key/truststore to file " + keyStoreFile, e); 261 } 262 } 263}