From 99b7b80f1dae79d79f71a5c3b56bfcae8bcf6098 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Sat, 6 Feb 2016 17:10:00 +0100 Subject: [PATCH] #450 Fix copying of JAR files to plugin folder - Create SettingsMigrationService#copyFileFromResource (inspired from CustomSettings) - Use new method to copy missing files in plugin folder from JAR - Create YamlFileConfiguration inside NewSetting: FileConfiguration object provided by JavaPlugin#getConfig() sets default values from the JAR's config.yml :( - Change ConsoleLogger to take logger from plugin (work in progress) --- pom.xml | 1 + src/main/java/fr/xephi/authme/AuthMe.java | 18 ++++- .../java/fr/xephi/authme/ConsoleLogger.java | 30 ++++--- .../fr/xephi/authme/settings/NewSetting.java | 78 ++++++++----------- .../fr/xephi/authme/settings/Settings.java | 23 +----- .../settings/SettingsMigrationService.java | 47 ++++++++++- src/main/resources/welcome.txt | 3 + .../authme/ConsoleLoggerTestInitializer.java | 20 +++++ .../xephi/authme/output/Log4JFilterTest.java | 4 +- .../output/MessagesIntegrationTest.java | 10 ++- .../authme/security/crypts/BcryptTest.java | 2 + .../authme/security/crypts/XFBCRYPTTest.java | 4 +- .../java/fr/xephi/authme/util/UtilsTest.java | 2 + 13 files changed, 156 insertions(+), 86 deletions(-) create mode 100644 src/main/resources/welcome.txt create mode 100644 src/test/java/fr/xephi/authme/ConsoleLoggerTestInitializer.java diff --git a/pom.xml b/pom.xml index 7a77f60ff..9bbbd39b5 100644 --- a/pom.xml +++ b/pom.xml @@ -97,6 +97,7 @@ src/main/resources/ email.html + welcome.txt diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 980e3e955..c5a26048c 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -13,6 +13,7 @@ import java.util.logging.Logger; import com.google.common.base.Charsets; import com.google.common.io.Resources; +import fr.xephi.authme.settings.SettingsMigrationService; import org.apache.logging.log4j.LogManager; import org.bukkit.Bukkit; @@ -207,11 +208,21 @@ public class AuthMe extends JavaPlugin { // Set various instances server = getServer(); plugin = this; + ConsoleLogger.setLogger(getLogger()); setPluginInfos(); // Load settings and custom configurations, if it fails, stop the server due to security reasons. newSettings = createNewSetting(); + if (newSettings == null) { + ConsoleLogger.showError("Could not load configuration. Aborting."); + server.shutdown(); + return; + } + ConsoleLogger.setLoggingOptions(newSettings.getProperty(SecuritySettings.USE_LOGGING), + new File(getDataFolder(), "authme.log")); + + // Old settings manager if (!loadSettings()) { server.shutdown(); setEnabled(false); @@ -425,7 +436,6 @@ public class AuthMe extends JavaPlugin { private boolean loadSettings() { try { settings = new Settings(this); - Settings.reload(); return true; } catch (Exception e) { ConsoleLogger.logException("Can't load the configuration file... Something went wrong. " @@ -436,8 +446,10 @@ public class AuthMe extends JavaPlugin { } private NewSetting createNewSetting() { - File configFile = new File(getDataFolder() + "config.yml"); - return new NewSetting(getConfig(), configFile, getDataFolder()); + File configFile = new File(getDataFolder(), "config.yml"); + return SettingsMigrationService.copyFileFromResource(configFile, "config.yml") + ? new NewSetting(configFile, getDataFolder()) + : null; } /** diff --git a/src/main/java/fr/xephi/authme/ConsoleLogger.java b/src/main/java/fr/xephi/authme/ConsoleLogger.java index 605cabc08..eca20fbba 100644 --- a/src/main/java/fr/xephi/authme/ConsoleLogger.java +++ b/src/main/java/fr/xephi/authme/ConsoleLogger.java @@ -1,16 +1,16 @@ package fr.xephi.authme; import com.google.common.base.Throwables; -import fr.xephi.authme.settings.Settings; import fr.xephi.authme.util.StringUtils; -import fr.xephi.authme.util.Wrapper; +import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.StandardOpenOption; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.logging.Logger; /** * The plugin's static logger. @@ -19,21 +19,31 @@ public final class ConsoleLogger { private static final String NEW_LINE = System.getProperty("line.separator"); private static final DateFormat DATE_FORMAT = new SimpleDateFormat("[MM-dd HH:mm:ss]"); - - private static Wrapper wrapper = Wrapper.getInstance(); + private static Logger logger; + private static boolean useLogging = false; + private static File logFile; private ConsoleLogger() { // Service class } + public static void setLogger(Logger logger) { + ConsoleLogger.logger = logger; + } + + public static void setLoggingOptions(boolean useLogging, File logFile) { + ConsoleLogger.useLogging = useLogging; + ConsoleLogger.logFile = logFile; + } + /** * Print an info message. * * @param message String */ public static void info(String message) { - wrapper.getLogger().info(message); - if (Settings.useLogging) { + logger.info(message); + if (useLogging) { writeLog(message); } } @@ -44,8 +54,8 @@ public final class ConsoleLogger { * @param message String */ public static void showError(String message) { - wrapper.getLogger().warning(message); - if (Settings.useLogging) { + logger.warning(message); + if (useLogging) { writeLog("ERROR: " + message); } } @@ -61,7 +71,7 @@ public final class ConsoleLogger { dateTime = DATE_FORMAT.format(new Date()); } try { - Files.write(Settings.LOG_FILE.toPath(), (dateTime + ": " + message + NEW_LINE).getBytes(), + Files.write(logFile.toPath(), (dateTime + ": " + message + NEW_LINE).getBytes(), StandardOpenOption.APPEND, StandardOpenOption.CREATE); } catch (IOException ignored) { @@ -74,7 +84,7 @@ public final class ConsoleLogger { * @param th The Throwable whose stack trace should be logged */ public static void writeStackTrace(Throwable th) { - if (Settings.useLogging) { + if (useLogging) { writeLog(Throwables.getStackTraceAsString(th)); } } diff --git a/src/main/java/fr/xephi/authme/settings/NewSetting.java b/src/main/java/fr/xephi/authme/settings/NewSetting.java index 85ae65058..29e833d0a 100644 --- a/src/main/java/fr/xephi/authme/settings/NewSetting.java +++ b/src/main/java/fr/xephi/authme/settings/NewSetting.java @@ -24,7 +24,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; -import static fr.xephi.authme.util.StringUtils.makePath; +import static fr.xephi.authme.settings.SettingsMigrationService.copyFileFromResource; /** * The new settings manager. @@ -42,24 +42,14 @@ public class NewSetting { /** * Constructor. Checks the given {@link FileConfiguration} object for completeness. * - * @param configuration The configuration to interact with * @param configFile The configuration file * @param pluginFolder The AuthMe plugin folder */ - public NewSetting(FileConfiguration configuration, File configFile, File pluginFolder) { - this.configuration = configuration; + public NewSetting(File configFile, File pluginFolder) { + this.configuration = YamlConfiguration.loadConfiguration(configFile); this.configFile = configFile; this.pluginFolder = pluginFolder; - messagesFile = buildMessagesFile(); - welcomeMessage = readWelcomeMessage(); - emailMessage = readEmailMessage(); - - PropertyMap propertyMap = SettingsFieldRetriever.getAllPropertyFields(); - if (SettingsMigrationService.checkAndMigrate(configuration, propertyMap, pluginFolder)) { - ConsoleLogger.info("Merged new config options"); - ConsoleLogger.info("Please check your config.yml file for new settings!"); - save(propertyMap); - } + validateAndLoadOptions(); } /** @@ -131,6 +121,7 @@ public class NewSetting { */ public void reload() { configuration = YamlConfiguration.loadConfiguration(configFile); + validateAndLoadOptions(); } private void save(PropertyMap propertyMap) { @@ -185,6 +176,19 @@ public class NewSetting { } } + private void validateAndLoadOptions() { + PropertyMap propertyMap = SettingsFieldRetriever.getAllPropertyFields(); + if (SettingsMigrationService.checkAndMigrate(configuration, propertyMap, pluginFolder)) { + ConsoleLogger.info("Merged new config options"); + ConsoleLogger.info("Please check your config.yml file for new settings!"); + save(propertyMap); + } + + messagesFile = buildMessagesFile(); + welcomeMessage = readWelcomeMessage(); + emailMessage = readEmailMessage(); + } + private String toYaml(Property property, int indent, Yaml simpleYaml, Yaml singleQuoteYaml) { String representation = property.toYaml(configuration, simpleYaml, singleQuoteYaml); return join("\n" + indent(indent), representation.split("\\n")); @@ -196,59 +200,45 @@ public class NewSetting { if (messagesFile.exists()) { return messagesFile; } - return buildMessagesFileFromCode("en"); + + return copyFileFromResource(messagesFile, buildMessagesFilePathFromCode(languageCode)) + ? messagesFile + : buildMessagesFileFromCode("en"); } private File buildMessagesFileFromCode(String language) { - return new File(pluginFolder, - makePath("messages", "messages_" + language + ".yml")); + return new File(pluginFolder, buildMessagesFilePathFromCode(language)); + } + + private static String buildMessagesFilePathFromCode(String language) { + return StringUtils.makePath("messages", "messages_" + language + ".yml"); } private List readWelcomeMessage() { if (getProperty(RegistrationSettings.USE_WELCOME_MESSAGE)) { final File welcomeFile = new File(pluginFolder, "welcome.txt"); final Charset charset = Charset.forName("UTF-8"); - if (!welcomeFile.exists()) { + if (copyFileFromResource(welcomeFile, "welcome.txt")) { try { - Files.write( - "Welcome {PLAYER} to {SERVER} server\n\nThis server uses AuthMe protection!", - welcomeFile, charset); + return Files.readLines(welcomeFile, charset); } catch (IOException e) { - ConsoleLogger.showError("Failed to create file '" + welcomeFile.getPath() + "': " - + StringUtils.formatException(e)); - ConsoleLogger.writeStackTrace(e); + ConsoleLogger.logException("Failed to read file '" + welcomeFile.getPath() + "':", e); } } - try { - return Files.readLines(welcomeFile, charset); - } catch (IOException e) { - ConsoleLogger.showError("Failed to read file '" + welcomeFile.getPath() + "': " + - StringUtils.formatException(e)); - ConsoleLogger.writeStackTrace(e); - } } return new ArrayList<>(0); } private String readEmailMessage() { - final File emailFile = new File(pluginFolder, "email.txt"); + final File emailFile = new File(pluginFolder, "email.html"); final Charset charset = Charset.forName("UTF-8"); - if (!emailFile.exists()) { + if (copyFileFromResource(emailFile, "email.html")) { try { - Files.write("", emailFile, charset); + return StringUtils.join("", Files.readLines(emailFile, charset)); } catch (IOException e) { - ConsoleLogger.showError("Failed to create file '" + emailFile.getPath() + "': " - + StringUtils.formatException(e)); - ConsoleLogger.writeStackTrace(e); + ConsoleLogger.logException("Failed to read file '" + emailFile.getPath() + "':", e); } } - try { - return StringUtils.join("", Files.readLines(emailFile, charset)); - } catch (IOException e) { - ConsoleLogger.showError("Failed to read file '" + emailFile.getPath() + "': " + - StringUtils.formatException(e)); - ConsoleLogger.writeStackTrace(e); - } return ""; } diff --git a/src/main/java/fr/xephi/authme/settings/Settings.java b/src/main/java/fr/xephi/authme/settings/Settings.java index 96d15d12e..ff7116213 100644 --- a/src/main/java/fr/xephi/authme/settings/Settings.java +++ b/src/main/java/fr/xephi/authme/settings/Settings.java @@ -6,7 +6,7 @@ import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource.DataSourceType; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.util.Wrapper; -import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.configuration.file.FileConfiguration; import java.io.File; import java.io.IOException; @@ -87,7 +87,7 @@ public final class Settings { getmaxRegPerEmail, bCryptLog2Rounds, getPhpbbGroup, antiBotSensibility, antiBotDuration, delayRecall, getMaxLoginPerIp, getMaxJoinPerIp; - protected static YamlConfiguration configFile; + protected static FileConfiguration configFile; private static AuthMe plugin; private static Settings instance; @@ -99,25 +99,8 @@ public final class Settings { public Settings(AuthMe pl) { instance = this; plugin = pl; - configFile = (YamlConfiguration) plugin.getConfig(); - } - - /** - * Method reload. - * - * @throws Exception if something went wrong - */ - public static void reload() throws Exception { - plugin.getLogger().info("Loading Configuration File..."); - boolean exist = SETTINGS_FILE.exists(); - if (!exist) { - plugin.saveDefaultConfig(); - } - configFile.load(SETTINGS_FILE); + configFile = plugin.getConfig(); loadVariables(); - if (exist) { - instance.saveDefaults(); - } } public static void loadVariables() { diff --git a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java index 1e010f4c5..0a41301c7 100644 --- a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java +++ b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java @@ -1,17 +1,20 @@ package fr.xephi.authme.settings; import com.google.common.annotations.VisibleForTesting; +import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.propertymap.PropertyMap; -import fr.xephi.authme.util.StringUtils; import org.bukkit.configuration.file.FileConfiguration; import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; import static fr.xephi.authme.settings.properties.RestrictionSettings.ALLOWED_NICKNAME_CHARACTERS; +import static java.lang.String.format; /** * Service for verifying that the configuration is up-to-date. @@ -86,15 +89,51 @@ public final class SettingsMigrationService { } final File emailFile = new File(dataFolder, "email.html"); + final String mailText = configuration.getString(oldSettingPath) + .replace("", "") + .replace("", "") + .replace("", "") + .replace("", ""); if (!emailFile.exists()) { try (FileWriter fw = new FileWriter(emailFile)) { - fw.write(configuration.getString("Email.mailText")); + fw.write(mailText); } catch (IOException e) { - ConsoleLogger.showError("Could not create email.html configuration file: " - + StringUtils.formatException(e)); + ConsoleLogger.logException("Could not create email.html configuration file:", e); } } return true; } + /** + * Copy a resource file (from the JAR) to the given file if it doesn't exist. + * + * @param destinationFile The file to check and copy to (outside of JAR) + * @param resourcePath Absolute path to the resource file (path to file within JAR) + * @return False if the file does not exist and could not be copied, true otherwise + */ + public static boolean copyFileFromResource(File destinationFile, String resourcePath) { + if (destinationFile.exists()) { + return true; + } else if (!destinationFile.getParentFile().exists() && !destinationFile.getParentFile().mkdirs()) { + ConsoleLogger.showError("Cannot create parent directories for '" + destinationFile + "'"); + return false; + } + + // ClassLoader#getResourceAsStream does not deal with the '\' path separator: replace to '/' + final String normalizedPath = resourcePath.replace("\\", "/"); + try (InputStream is = AuthMe.class.getClassLoader().getResourceAsStream(normalizedPath)) { + if (is == null) { + ConsoleLogger.showError(format("Cannot copy resource '%s' to file '%s': cannot load resource", + resourcePath, destinationFile.getPath())); + } else { + Files.copy(is, destinationFile.toPath()); + return true; + } + } catch (IOException e) { + ConsoleLogger.logException(format("Cannot copy resource '%s' to file '%s':", + resourcePath, destinationFile.getPath()), e); + } + return false; + } + } diff --git a/src/main/resources/welcome.txt b/src/main/resources/welcome.txt new file mode 100644 index 000000000..1c49f042b --- /dev/null +++ b/src/main/resources/welcome.txt @@ -0,0 +1,3 @@ +Welcome {PLAYER} on {SERVER} server + +This server uses AuthMeReloaded protection! diff --git a/src/test/java/fr/xephi/authme/ConsoleLoggerTestInitializer.java b/src/test/java/fr/xephi/authme/ConsoleLoggerTestInitializer.java new file mode 100644 index 000000000..cfa871e3e --- /dev/null +++ b/src/test/java/fr/xephi/authme/ConsoleLoggerTestInitializer.java @@ -0,0 +1,20 @@ +package fr.xephi.authme; + +import org.mockito.Mockito; + +import java.util.logging.Logger; + +/** + * Test initializer for {@link ConsoleLogger}. + */ +public class ConsoleLoggerTestInitializer { + + private ConsoleLoggerTestInitializer() { + } + + public static Logger setupLogger() { + Logger logger = Mockito.mock(Logger.class); + ConsoleLogger.setLogger(logger); + return logger; + } +} diff --git a/src/test/java/fr/xephi/authme/output/Log4JFilterTest.java b/src/test/java/fr/xephi/authme/output/Log4JFilterTest.java index fdd338919..b9977de8e 100644 --- a/src/test/java/fr/xephi/authme/output/Log4JFilterTest.java +++ b/src/test/java/fr/xephi/authme/output/Log4JFilterTest.java @@ -219,8 +219,8 @@ public class Log4JFilterTest { * Mocks a {@link Message} object and makes it return the given formatted message. * * @param formattedMessage the formatted message the mock should return - - * @return Message mock */ + * @return Message mock + */ private static Message mockMessage(String formattedMessage) { Message message = Mockito.mock(Message.class); when(message.getFormattedMessage()).thenReturn(formattedMessage); diff --git a/src/test/java/fr/xephi/authme/output/MessagesIntegrationTest.java b/src/test/java/fr/xephi/authme/output/MessagesIntegrationTest.java index 6e63a934d..2d3e7b08f 100644 --- a/src/test/java/fr/xephi/authme/output/MessagesIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/output/MessagesIntegrationTest.java @@ -1,9 +1,11 @@ package fr.xephi.authme.output; +import fr.xephi.authme.ConsoleLoggerTestInitializer; import fr.xephi.authme.util.WrapperMock; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; @@ -27,6 +29,12 @@ public class MessagesIntegrationTest { private static final String YML_TEST_FILE = "messages_test.yml"; private Messages messages; + @BeforeClass + public static void setup() { + WrapperMock.createInstance(); + ConsoleLoggerTestInitializer.setupLogger(); + } + /** * Loads the messages in the file {@code messages_test.yml} in the test resources folder. * The file does not contain all messages defined in {@link MessageKey} and its contents @@ -34,8 +42,6 @@ public class MessagesIntegrationTest { */ @Before public void setUpMessages() { - WrapperMock.createInstance(); - URL url = getClass().getClassLoader().getResource(YML_TEST_FILE); if (url == null) { throw new RuntimeException("File '" + YML_TEST_FILE + "' could not be loaded"); diff --git a/src/test/java/fr/xephi/authme/security/crypts/BcryptTest.java b/src/test/java/fr/xephi/authme/security/crypts/BcryptTest.java index 106689ae7..6ee291143 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/BcryptTest.java +++ b/src/test/java/fr/xephi/authme/security/crypts/BcryptTest.java @@ -1,5 +1,6 @@ package fr.xephi.authme.security.crypts; +import fr.xephi.authme.ConsoleLoggerTestInitializer; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.util.WrapperMock; import org.junit.BeforeClass; @@ -13,6 +14,7 @@ public class BcryptTest extends AbstractEncryptionMethodTest { public static void setUpSettings() { WrapperMock.createInstance(); Settings.bCryptLog2Rounds = 8; + ConsoleLoggerTestInitializer.setupLogger(); } public BcryptTest() { diff --git a/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java b/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java index 3b7294a04..ed49ccd71 100644 --- a/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java +++ b/src/test/java/fr/xephi/authme/security/crypts/XFBCRYPTTest.java @@ -1,5 +1,6 @@ package fr.xephi.authme.security.crypts; +import fr.xephi.authme.ConsoleLoggerTestInitializer; import fr.xephi.authme.util.WrapperMock; import org.junit.BeforeClass; @@ -9,8 +10,9 @@ import org.junit.BeforeClass; public class XFBCRYPTTest extends AbstractEncryptionMethodTest { @BeforeClass - public static void setUpWrapper() { + public static void setup() { WrapperMock.createInstance(); + ConsoleLoggerTestInitializer.setupLogger(); } public XFBCRYPTTest() { diff --git a/src/test/java/fr/xephi/authme/util/UtilsTest.java b/src/test/java/fr/xephi/authme/util/UtilsTest.java index 526e511fe..67723c494 100644 --- a/src/test/java/fr/xephi/authme/util/UtilsTest.java +++ b/src/test/java/fr/xephi/authme/util/UtilsTest.java @@ -1,6 +1,7 @@ package fr.xephi.authme.util; import fr.xephi.authme.AuthMe; +import fr.xephi.authme.ConsoleLoggerTestInitializer; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.settings.NewSetting; @@ -39,6 +40,7 @@ public class UtilsTest { public static void setUpMocks() { WrapperMock wrapperMock = WrapperMock.createInstance(); authMeMock = wrapperMock.getAuthMe(); + ConsoleLoggerTestInitializer.setupLogger(); } @Before