diff --git a/.floo b/.floo deleted file mode 100644 index b0f2c28e0..000000000 --- a/.floo +++ /dev/null @@ -1,3 +0,0 @@ -{ - "url": "https://floobits.com/AuthMe-Team/AuthMeReloaded" -} \ No newline at end of file diff --git a/.flooignore b/.flooignore deleted file mode 100644 index e9c355d00..000000000 --- a/.flooignore +++ /dev/null @@ -1,123 +0,0 @@ -### Java ### -*.class - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -#*.jar -*.war -*.ear - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* - - -### Intellij ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm - -*.iml - -## Directory-based project format: -.idea/ -# if you remove the above rule, at least ignore the following: - -# User-specific stuff: -# .idea/workspace.xml -# .idea/tasks.xml -# .idea/dictionaries - -# Sensitive or high-churn files: -# .idea/dataSources.ids -# .idea/dataSources.xml -# .idea/sqlDataSources.xml -# .idea/dynamic.xml -# .idea/uiDesigner.xml - -# Gradle: -# .idea/gradle.xml -# .idea/libraries - -# Mongo Explorer plugin: -# .idea/mongoSettings.xml - -## File-based project format: -*.ipr -*.iws - -## Plugin-specific files: - -# IntelliJ -/out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties - - -### Eclipse ### -*.pydevproject -.metadata -.gradle -bin/ -tmp/ -*.tmp -*.bak -*.swp -*~.nib -local.properties -.settings/ -.loadpath - -# Eclipse Core -.project - -# External tool builders -.externalToolBuilders/ - -# Locally stored "Eclipse launch configurations" -*.launch - -# CDT-specific -.cproject - -# JDT-specific (Eclipse Java Development Tools) -.classpath - -# PDT-specific -.buildpath - -# sbteclipse plugin -.target - -# TeXlipse plugin -.texlipse - - -### Maven ### -target/ -pom.xml.tag -pom.xml.releaseBackup -pom.xml.versionsBackup -pom.xml.next -release.properties -dependency-reduced-pom.xml -buildNumber.properties - - -### NetBeans ### -nbproject/private/ -build/ -nbbuild/ -dist/ -nbdist/ -nbactions.xml -nb-configuration.xml -.nb-gradle/ diff --git a/README.md b/README.md index da3267fbe..d648a6bcc 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ - Build status: [![Build Status](https://travis-ci.org/Xephi/AuthMeReloaded.svg?branch=master)](https://travis-ci.org/Xephi/AuthMeReloaded) [![Dependency Status](https://www.versioneye.com/user/projects/55bab9e8653762002000190a/badge.svg?style=flat)](https://www.versioneye.com/user/projects/55bab9e8653762002000190a) - Build status (CircleCI): [![Circle CI](https://circleci.com/gh/Xephi/AuthMeReloaded.svg?style=svg)](https://circleci.com/gh/Xephi/AuthMeReloaded) +- Alternative Dev Build download link (via CircleCi): Download - Code Coverage: [![Coverage Status](https://coveralls.io/repos/AuthMe-Team/AuthMeReloaded/badge.svg?branch=master&service=github)](https://coveralls.io/github/AuthMe-Team/AuthMeReloaded?branch=master) diff --git a/circle.yml b/circle.yml index 672011c2e..da709f387 100644 --- a/circle.yml +++ b/circle.yml @@ -7,7 +7,9 @@ general: test: override: - mvn clean install -B - + post: + - cp ./target/AuthMe*.jar $CIRCLE_ARTIFACTS + - cp ./target/AuthMe-*-SNAPSHOT.jar $CIRCLE_ARTIFACTS/AuthMe.jar notify: webhooks: - url: https://webhooks.gitter.im/e/7b92ac1a1741748b26bf diff --git a/pom.xml b/pom.xml index 7e8cd70ba..c4118541e 100644 --- a/pom.xml +++ b/pom.xml @@ -97,6 +97,7 @@ src/main/resources/ email.html + welcome.txt @@ -125,73 +126,22 @@ - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.codehaus.mojo - buildnumber-maven-plugin - [1.0,) - - create-timestamp - - - - - true - true - - - - - - - - - - org.apache.maven.plugins maven-compiler-plugin - 3.3 + 3.5 1.7 ${javaVersion} - - org.codehaus.mojo - buildnumber-maven-plugin - 1.4 - - dd-MM-yy_HH-mm - build.time - - - - generate-resources - - create-timestamp - - - - - org.apache.maven.plugins maven-shade-plugin - 2.4.2 + 2.4.3 false false @@ -263,6 +213,14 @@ UTF-8 true + + + attach-javadocs + + jar + + + @@ -271,7 +229,7 @@ spigot-repo - http://hub.spigotmc.org/nexus/content/groups/public + https://hub.spigotmc.org/nexus/content/repositories/snapshots @@ -341,7 +299,7 @@ org.slf4j slf4j-jdk14 - 1.7.13 + 1.7.14 compile true @@ -378,7 +336,7 @@ com.maxmind.geoip geoip-api - 1.2.15 + 1.3.0 compile true diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 1b39b3440..c5a26048c 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -1,9 +1,36 @@ package fr.xephi.authme; -import com.earth2me.essentials.Essentials; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.Calendar; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +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; +import org.bukkit.Location; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitTask; + +import com.earth2me.essentials.Essentials; import com.onarandombox.MultiverseCore.MultiverseCore; +import net.minelink.ctplus.CombatTagPlus; + import fr.xephi.authme.api.API; import fr.xephi.authme.api.NewAPI; import fr.xephi.authme.cache.auth.PlayerAuth; @@ -34,7 +61,6 @@ import fr.xephi.authme.listener.AuthMePlayerListener18; import fr.xephi.authme.listener.AuthMeServerListener; import fr.xephi.authme.listener.AuthMeTabCompletePacketAdapter; import fr.xephi.authme.mail.SendMailSSL; -import fr.xephi.authme.modules.ModuleManager; import fr.xephi.authme.output.ConsoleFilter; import fr.xephi.authme.output.Log4JFilter; import fr.xephi.authme.output.MessageKey; @@ -45,41 +71,24 @@ import fr.xephi.authme.process.Management; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.security.PasswordSecurity; import fr.xephi.authme.security.crypts.HashedPassword; +import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.OtherAccounts; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.settings.Spawn; -import fr.xephi.authme.settings.custom.NewSetting; +import fr.xephi.authme.settings.properties.DatabaseSettings; +import fr.xephi.authme.settings.properties.HooksSettings; +import fr.xephi.authme.settings.properties.PluginSettings; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import fr.xephi.authme.settings.properties.SecuritySettings; +import fr.xephi.authme.util.CollectionUtils; import fr.xephi.authme.util.GeoLiteAPI; import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.Wrapper; -import net.minelink.ctplus.CombatTagPlus; -import org.apache.logging.log4j.LogManager; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Server; -import org.bukkit.World; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.java.JavaPlugin; -import org.bukkit.scheduler.BukkitTask; -import org.mcstats.Metrics; -import org.mcstats.Metrics.Graph; -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.util.Calendar; -import java.util.Collection; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.logging.Level; -import java.util.logging.Logger; +import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_ACCOUNT; +import static fr.xephi.authme.settings.properties.EmailSettings.MAIL_PASSWORD; +import static fr.xephi.authme.settings.properties.PluginSettings.HELP_HEADER; /** * The AuthMe main class. @@ -104,7 +113,6 @@ public class AuthMe extends JavaPlugin { private NewSetting newSettings; private Messages messages; private JsonCache playerBackup; - private ModuleManager moduleManager; private PasswordSecurity passwordSecurity; private DataSource database; @@ -170,15 +178,6 @@ public class AuthMe extends JavaPlugin { return pluginBuildNumber; } - /** - * Get the plugin's Settings. - * - * @return Plugin's settings. - */ - public Settings getSettings() { - return settings; - } - /** * Get the Messages instance. * @@ -209,27 +208,35 @@ 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. - if (loadSettings()) { + 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); return; } - newSettings = createNewSetting(); - // Set up messages & password security - messages = Messages.getInstance(); + messages = new Messages(newSettings.getMessagesFile()); // Connect to the database and setup tables try { setupDatabase(); } catch (Exception e) { - ConsoleLogger.writeStackTrace(e); - ConsoleLogger.showError(e.getMessage()); - ConsoleLogger.showError("Fatal error occurred during database connection! Authme initialization ABORTED!"); + ConsoleLogger.logException("Fatal error occurred during database connection! " + + "Authme initialization aborted!", e); stopOrUnload(); return; } @@ -241,15 +248,11 @@ public class AuthMe extends JavaPlugin { permsMan = initializePermissionsManager(); commandHandler = initializeCommandHandler(permsMan, messages, passwordSecurity, newSettings); - // Set up the module manager - setupModuleManager(); - // Setup otherAccounts file this.otherAccounts = OtherAccounts.getInstance(); - // Set up Metrics - setupMetrics(); + MetricsStarter.setupMetrics(plugin, newSettings); // Set console filter setupConsoleFilter(); @@ -279,7 +282,7 @@ public class AuthMe extends JavaPlugin { // End of Hooks // Do a backup on start - new PerformBackup(plugin).doBackup(PerformBackup.BackupCause.START); + new PerformBackup(plugin, newSettings).doBackup(PerformBackup.BackupCause.START); // Setup the inventory backup @@ -292,7 +295,7 @@ public class AuthMe extends JavaPlugin { setupApi(); // Set up the management - management = new Management(this); + management = new Management(this, newSettings); // Set up the BungeeCord hook setupBungeeCordHook(); @@ -321,32 +324,14 @@ public class AuthMe extends JavaPlugin { ConsoleLogger.info("AuthMe " + this.getDescription().getVersion() + " correctly enabled!"); } - /** - * Set up the module manager. - */ - private void setupModuleManager() { - // TODO: Clean this up! - // TODO: split the plugin in more modules - // TODO: log number of loaded modules - - // Define the module manager instance - moduleManager = new ModuleManager(this); - - // Load the modules - // int loaded = moduleManager.loadModules(); - } - /** * Set up the mail API, if enabled. */ private void setupMailApi() { // Make sure the mail API is enabled - if (Settings.getmailAccount.isEmpty() || Settings.getmailPassword.isEmpty()) { - return; + if (!newSettings.getProperty(MAIL_ACCOUNT).isEmpty() && !newSettings.getProperty(MAIL_PASSWORD).isEmpty()) { + this.mail = new SendMailSSL(this, newSettings); } - - // Set up the mail API - this.mail = new SendMailSSL(this); } /** @@ -354,12 +339,13 @@ public class AuthMe extends JavaPlugin { */ private void showSettingsWarnings() { // Force single session disabled - if (!Settings.isForceSingleSessionEnabled) { + if (!newSettings.getProperty(RestrictionSettings.FORCE_SINGLE_SESSION)) { ConsoleLogger.showError("WARNING!!! By disabling ForceSingleSession, your server protection is inadequate!"); } // Session timeout disabled - if (Settings.getSessionTimeout == 0 && Settings.isSessionsEnabled) { + if (newSettings.getProperty(PluginSettings.SESSIONS_TIMEOUT) == 0 + && newSettings.getProperty(PluginSettings.SESSIONS_ENABLED)) { ConsoleLogger.showError("WARNING!!! You set session timeout to 0, this may cause security issues!"); } } @@ -414,7 +400,7 @@ public class AuthMe extends JavaPlugin { * Set up the BungeeCord hook. */ private void setupBungeeCordHook() { - if (Settings.bungee) { + if (newSettings.getProperty(HooksSettings.BUNGEECORD)) { Bukkit.getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); Bukkit.getMessenger().registerIncomingPluginChannel(this, "BungeeCord", new BungeeCordMessage(this)); } @@ -422,7 +408,7 @@ public class AuthMe extends JavaPlugin { private CommandHandler initializeCommandHandler(PermissionsManager permissionsManager, Messages messages, PasswordSecurity passwordSecurity, NewSetting settings) { - HelpProvider helpProvider = new HelpProvider(permissionsManager); + HelpProvider helpProvider = new HelpProvider(permissionsManager, settings.getProperty(HELP_HEADER)); Set baseCommands = CommandInitializer.buildCommands(); CommandMapper mapper = new CommandMapper(baseCommands, permissionsManager); CommandService commandService = new CommandService( @@ -450,20 +436,20 @@ public class AuthMe extends JavaPlugin { private boolean loadSettings() { try { settings = new Settings(this); - Settings.reload(); - } catch (Exception e) { - ConsoleLogger.writeStackTrace(e); - ConsoleLogger.showError("Can't load the configuration file... Something went wrong. " - + "To avoid security issues the server will shut down!"); - server.shutdown(); return true; + } catch (Exception e) { + ConsoleLogger.logException("Can't load the configuration file... Something went wrong. " + + "To avoid security issues the server will shut down!", e); + server.shutdown(); } return false; } private NewSetting createNewSetting() { - File configFile = new File(getDataFolder() + "config.yml"); - return new NewSetting(getConfig(), configFile); + File configFile = new File(getDataFolder(), "config.yml"); + return SettingsMigrationService.copyFileFromResource(configFile, "config.yml") + ? new NewSetting(configFile, getDataFolder()) + : null; } /** @@ -485,42 +471,6 @@ public class AuthMe extends JavaPlugin { } } - /** - * Set up Metrics. - */ - private void setupMetrics() { - try { - Metrics metrics = new Metrics(this); - Graph messagesLanguage = metrics.createGraph("Messages language"); - Graph databaseBackend = metrics.createGraph("Database backend"); - - // Custom graphs - if (Settings.messageFile.exists()) { - messagesLanguage.addPlotter(new Metrics.Plotter(Settings.messagesLanguage) { - - @Override - public int getValue() { - return 1; - } - }); - } - databaseBackend.addPlotter(new Metrics.Plotter(Settings.getDataSource.toString()) { - - @Override - public int getValue() { - return 1; - } - }); - - metrics.start(); - ConsoleLogger.info("Metrics started successfully!"); - } catch (Exception e) { - // Failed to submit the metrics data - ConsoleLogger.writeStackTrace(e); - ConsoleLogger.showError("Can't start Metrics! The plugin will work anyway..."); - } - } - @Override public void onDisable() { // Save player data @@ -530,47 +480,10 @@ public class AuthMe extends JavaPlugin { } // Do backup on stop if enabled - new PerformBackup(plugin).doBackup(PerformBackup.BackupCause.STOP); - - // Unload modules - if (moduleManager != null) { - moduleManager.unloadModules(); + if (newSettings != null) { + new PerformBackup(plugin, newSettings).doBackup(PerformBackup.BackupCause.STOP); } - List pendingTasks = getServer().getScheduler().getPendingTasks(); - for (Iterator iterator = pendingTasks.iterator(); iterator.hasNext();) { - BukkitTask pendingTask = iterator.next(); - if (!pendingTask.getOwner().equals(this) || pendingTask.isSync()) { - //remove all unrelevant tasks - iterator.remove(); - } - } - - getLogger().log(Level.INFO, "Waiting for {0} tasks to finish", pendingTasks.size()); - int progress = 0; - try { - for (BukkitTask pendingTask : pendingTasks) { - int maxTries = 5; - int taskId = pendingTask.getTaskId(); - while (getServer().getScheduler().isCurrentlyRunning(taskId)) { - if (maxTries <= 0) { - getLogger().log(Level.INFO, "Async task {0} times out after to many tries", taskId); - break; - } - - //one second - Thread.sleep(1000); - maxTries--; - } - - progress++; - getLogger().log(Level.INFO, "Progress: {0} / {1}", new Object[]{progress, pendingTasks.size()}); - } - } catch (InterruptedException interruptedException) { - - } - - // Close the database if (database != null) { database.close(); @@ -595,7 +508,7 @@ public class AuthMe extends JavaPlugin { database.close(); // Backend MYSQL - FILE - SQLITE - SQLITEHIKARI boolean isSQLite = false; - switch (Settings.getDataSource) { + switch (newSettings.getProperty(DatabaseSettings.BACKEND)) { case FILE: database = new FlatFile(); break; @@ -624,7 +537,7 @@ public class AuthMe extends JavaPlugin { if (Settings.getDataSource == DataSource.DataSourceType.FILE) { ConsoleLogger.showError("FlatFile backend has been detected and is now deprecated, it will be changed " + "to SQLite... Connection will be impossible until conversion is done!"); - ForceFlatToSqlite converter = new ForceFlatToSqlite(database); + ForceFlatToSqlite converter = new ForceFlatToSqlite(database, newSettings); DataSource source = converter.run(); if (source != null) { database = source; @@ -632,7 +545,7 @@ public class AuthMe extends JavaPlugin { } // TODO: Move this to another place maybe ? - if (Settings.getPasswordHash == HashAlgorithm.PLAINTEXT) { + if (HashAlgorithm.PLAINTEXT == newSettings.getProperty(SecuritySettings.PASSWORD_HASH)) { ConsoleLogger.showError("Your HashAlgorithm has been detected as plaintext and is now deprecated; " + "it will be changed and hashed now to the AuthMe default hashing method"); for (PlayerAuth auth : database.getAllAuths()) { @@ -641,11 +554,11 @@ public class AuthMe extends JavaPlugin { auth.setPassword(hashedPassword); database.updatePassword(auth); } - Settings.setValue("settings.security.passwordHash", "SHA256"); - Settings.reload(); + newSettings.setProperty(SecuritySettings.PASSWORD_HASH, HashAlgorithm.SHA256); + newSettings.save(); } - if (Settings.isCachingEnabled) { + if (newSettings.getProperty(DatabaseSettings.USE_CACHING)) { database = new CacheDataSource(database); } } @@ -736,24 +649,20 @@ public class AuthMe extends JavaPlugin { // Check the presence of the ProtocolLib plugin public void checkProtocolLib() { if (!server.getPluginManager().isPluginEnabled("ProtocolLib")) { - if (Settings.protectInventoryBeforeLogInEnabled) { - ConsoleLogger.showError("WARNING!!! The protectInventory feature requires ProtocolLib! Disabling it..."); + if (newSettings.getProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN)) { + ConsoleLogger.showError("WARNING! The protectInventory feature requires ProtocolLib! Disabling it..."); Settings.protectInventoryBeforeLogInEnabled = false; - getSettings().set("settings.restrictions.ProtectInventoryBeforeLogIn", false); + newSettings.setProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN, false); } return; } - if (Settings.protectInventoryBeforeLogInEnabled) { - if (inventoryProtector == null) { - inventoryProtector = new AuthMeInventoryPacketAdapter(this); - inventoryProtector.register(); - } - } else { - if (inventoryProtector != null) { - inventoryProtector.unregister(); - inventoryProtector = null; - } + if (newSettings.getProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN) && inventoryProtector == null) { + inventoryProtector = new AuthMeInventoryPacketAdapter(this); + inventoryProtector.register(); + } else if (inventoryProtector != null) { + inventoryProtector.unregister(); + inventoryProtector = null; } if (tabComplete == null) { tabComplete = new AuthMeTabCompletePacketAdapter(this); @@ -788,16 +697,14 @@ public class AuthMe extends JavaPlugin { PlayerCache.getInstance().removePlayer(name); } - // Select the player to kick when a vip player join the server when full + // Select the player to kick when a vip player joins the server when full public Player generateKickPlayer(Collection collection) { - Player player = null; - for (Player p : collection) { - if (!getPermissionsManager().hasPermission(p, PlayerPermission.IS_VIP)) { - player = p; - break; + for (Player player : collection) { + if (!getPermissionsManager().hasPermission(player, PlayerPermission.IS_VIP)) { + return player; } } - return player; + return null; } // Purge inactive players from the database, as defined in the configuration @@ -809,10 +716,7 @@ public class AuthMe extends JavaPlugin { calendar.add(Calendar.DATE, -(Settings.purgeDelay)); long until = calendar.getTimeInMillis(); List cleared = database.autoPurgeDatabase(until); - if (cleared == null) { - return; - } - if (cleared.isEmpty()) { + if (CollectionUtils.isEmpty(cleared)) { return; } ConsoleLogger.info("AutoPurging the Database: " + cleared.size() + " accounts removed!"); @@ -896,12 +800,12 @@ public class AuthMe extends JavaPlugin { for (Player player : Utils.getOnlinePlayers()) { if (player.isOnline()) { String name = player.getName().toLowerCase(); - if (database.isAuthAvailable(name)) - if (PlayerCache.getInstance().isAuthenticated(name)) { - String email = database.getAuth(name).getEmail(); - if (email == null || email.isEmpty() || email.equalsIgnoreCase("your@email.com")) - messages.send(player, MessageKey.ADD_EMAIL_MESSAGE); + if (database.isAuthAvailable(name) && PlayerCache.getInstance().isAuthenticated(name)) { + String email = database.getAuth(name).getEmail(); + if (email == null || email.isEmpty() || email.equalsIgnoreCase("your@email.com")) { + messages.send(player, MessageKey.ADD_EMAIL_MESSAGE); } + } } } } @@ -909,18 +813,18 @@ public class AuthMe extends JavaPlugin { } public String replaceAllInfo(String message, Player player) { - int playersOnline = Utils.getOnlinePlayers().size(); - message = message.replace("&", "\u00a7"); - message = message.replace("{PLAYER}", player.getName()); - message = message.replace("{ONLINE}", "" + playersOnline); - message = message.replace("{MAXPLAYERS}", "" + server.getMaxPlayers()); - message = message.replace("{IP}", getIP(player)); - message = message.replace("{LOGINS}", "" + PlayerCache.getInstance().getLogged()); - message = message.replace("{WORLD}", player.getWorld().getName()); - message = message.replace("{SERVER}", server.getServerName()); - message = message.replace("{VERSION}", server.getBukkitVersion()); - message = message.replace("{COUNTRY}", GeoLiteAPI.getCountryName(getIP(player))); - return message; + String playersOnline = Integer.toString(Utils.getOnlinePlayers().size()); + return message + .replace("&", "\u00a7") + .replace("{PLAYER}", player.getName()) + .replace("{ONLINE}", playersOnline) + .replace("{MAXPLAYERS}", Integer.toString(server.getMaxPlayers())) + .replace("{IP}", getIP(player)) + .replace("{LOGINS}", Integer.toString(PlayerCache.getInstance().getLogged())) + .replace("{WORLD}", player.getWorld().getName()) + .replace("{SERVER}", server.getServerName()) + .replace("{VERSION}", server.getBukkitVersion()) + .replace("{COUNTRY}", GeoLiteAPI.getCountryName(getIP(player))); } /** @@ -976,10 +880,6 @@ public class AuthMe extends JavaPlugin { return count >= Settings.getMaxJoinPerIp; } - public ModuleManager getModuleManager() { - return moduleManager; - } - /** * Handle Bukkit commands. * diff --git a/src/main/java/fr/xephi/authme/ConsoleLogger.java b/src/main/java/fr/xephi/authme/ConsoleLogger.java index fee14296d..eca20fbba 100644 --- a/src/main/java/fr/xephi/authme/ConsoleLogger.java +++ b/src/main/java/fr/xephi/authme/ConsoleLogger.java @@ -1,15 +1,16 @@ package fr.xephi.authme; import com.google.common.base.Throwables; -import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.util.Wrapper; +import fr.xephi.authme.util.StringUtils; +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. @@ -18,24 +19,33 @@ 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) { - return; + logger.info(message); + if (useLogging) { + writeLog(message); } - writeLog("" + message); } /** @@ -44,11 +54,10 @@ public final class ConsoleLogger { * @param message String */ public static void showError(String message) { - wrapper.getLogger().warning(message); - if (!Settings.useLogging) { - return; + logger.warning(message); + if (useLogging) { + writeLog("ERROR: " + message); } - writeLog("ERROR: " + message); } /** @@ -62,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) { @@ -72,12 +81,22 @@ public final class ConsoleLogger { /** * Write a StackTrace into the log. * - * @param ex Exception + * @param th The Throwable whose stack trace should be logged */ - public static void writeStackTrace(Exception ex) { - if (!Settings.useLogging) { - return; + public static void writeStackTrace(Throwable th) { + if (useLogging) { + writeLog(Throwables.getStackTraceAsString(th)); } - writeLog(Throwables.getStackTraceAsString(ex)); + } + + /** + * Logs a Throwable with the provided message and saves the stack trace to the log file. + * + * @param message The message to accompany the exception + * @param th The Throwable to log + */ + public static void logException(String message, Throwable th) { + showError(message + " " + StringUtils.formatException(th)); + writeStackTrace(th); } } diff --git a/src/main/java/fr/xephi/authme/MetricsStarter.java b/src/main/java/fr/xephi/authme/MetricsStarter.java new file mode 100644 index 000000000..eac9ebaf4 --- /dev/null +++ b/src/main/java/fr/xephi/authme/MetricsStarter.java @@ -0,0 +1,45 @@ +package fr.xephi.authme; + +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.DatabaseSettings; +import fr.xephi.authme.settings.properties.PluginSettings; +import org.mcstats.Metrics; +import org.mcstats.Metrics.Graph; + +import java.io.IOException; + +public class MetricsStarter { + + private MetricsStarter() { + } + + public static void setupMetrics(AuthMe plugin, NewSetting settings) { + try { + final Metrics metrics = new Metrics(plugin); + + final Graph languageGraph = metrics.createGraph("Messages Language"); + final String messagesLanguage = settings.getProperty(PluginSettings.MESSAGES_LANGUAGE); + languageGraph.addPlotter(new Metrics.Plotter(messagesLanguage) { + @Override + public int getValue() { + return 1; + } + }); + + final Graph databaseBackend = metrics.createGraph("Database Backend"); + final String dataSource = settings.getProperty(DatabaseSettings.BACKEND).toString(); + databaseBackend.addPlotter(new Metrics.Plotter(dataSource) { + @Override + public int getValue() { + return 1; + } + }); + + // Submit metrics + metrics.start(); + } catch (IOException e) { + // Failed to submit the metrics data + ConsoleLogger.logException("Can't start Metrics! The plugin will work anyway...", e); + } + } +} diff --git a/src/main/java/fr/xephi/authme/PerformBackup.java b/src/main/java/fr/xephi/authme/PerformBackup.java index 6dbb7e4f9..36b75d7d1 100644 --- a/src/main/java/fr/xephi/authme/PerformBackup.java +++ b/src/main/java/fr/xephi/authme/PerformBackup.java @@ -1,8 +1,17 @@ package fr.xephi.authme; -import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.datasource.DataSource; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.BackupSettings; +import fr.xephi.authme.settings.properties.DatabaseSettings; +import fr.xephi.authme.util.StringUtils; -import java.io.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; @@ -10,47 +19,56 @@ import java.util.Date; * The backup management class * * @author stefano - * @version $Revision: 1.0 $ */ public class PerformBackup { - final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH-mm"); - final String dateString = format.format(new Date()); - private final String dbName = Settings.getMySQLDatabase; - private final String dbUserName = Settings.getMySQLUsername; - private final String dbPassword = Settings.getMySQLPassword; - private final String tblname = Settings.getMySQLTablename; - private final String path = AuthMe.getInstance().getDataFolder() + File.separator + "backups" + File.separator + "backup" + dateString; - private AuthMe instance; + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH-mm"); + + private final String dbName; + private final String dbUserName; + private final String dbPassword; + private final String tblname; + private final String path; + private final File dataFolder; + private final NewSetting settings; /** * Constructor for PerformBackup. * * @param instance AuthMe */ - public PerformBackup(AuthMe instance) { - this.setInstance(instance); + public PerformBackup(AuthMe instance, NewSetting settings) { + this.dataFolder = instance.getDataFolder(); + this.settings = settings; + this.dbName = settings.getProperty(DatabaseSettings.MYSQL_DATABASE); + this.dbUserName = settings.getProperty(DatabaseSettings.MYSQL_USERNAME); + this.dbPassword = settings.getProperty(DatabaseSettings.MYSQL_PASSWORD); + this.tblname = settings.getProperty(DatabaseSettings.MYSQL_TABLE); + + String dateString = DATE_FORMAT.format(new Date()); + this.path = StringUtils.join(File.separator, + instance.getDataFolder().getPath(), "backups", "backup" + dateString); } /** * Perform a backup with the given reason. * - * @param cause BackupCause The cause of the backup. + * @param cause The cause of the backup. */ public void doBackup(BackupCause cause) { - if (!Settings.isBackupActivated) { - ConsoleLogger.showError("Can't perform a Backup: disabled in configuration. Cause of the Backup: " + cause.name()); + if (!settings.getProperty(BackupSettings.ENABLED)) { + // Print a warning if the backup was requested via command or by another plugin + if (cause == BackupCause.COMMAND || cause == BackupCause.OTHER) { + ConsoleLogger.showError("Can't perform a Backup: disabled in configuration. Cause of the Backup: " + + cause.name()); + } + return; } + // Check whether a backup should be made at the specified point in time - switch (cause) { - case START: - if (!Settings.isBackupOnStart) - return; - case STOP: - if (!Settings.isBackupOnStop) - return; - case COMMAND: - case OTHER: + if (BackupCause.START.equals(cause) && !settings.getProperty(BackupSettings.ON_SERVER_START) + || BackupCause.STOP.equals(cause) && !settings.getProperty(BackupSettings.ON_SERVER_STOP)) { + return; } // Do backup and check return value! @@ -61,37 +79,31 @@ public class PerformBackup { } } - /** - * Method doBackup. - * - * @return boolean - */ public boolean doBackup() { - - switch (Settings.getDataSource) { + DataSource.DataSourceType dataSourceType = settings.getProperty(DatabaseSettings.BACKEND); + switch (dataSourceType) { case FILE: - return FileBackup("auths.db"); + return fileBackup("auths.db"); case MYSQL: - return MySqlBackup(); + return mySqlBackup(); case SQLITE: - return FileBackup(Settings.getMySQLDatabase + ".db"); + return fileBackup(dbName + ".db"); + default: + ConsoleLogger.showError("Unknown data source type '" + dataSourceType + "' for backup"); } return false; } - /** - * Method MySqlBackup. - * - * @return boolean - */ - private boolean MySqlBackup() { - File dirBackup = new File(AuthMe.getInstance().getDataFolder() + "/backups"); + private boolean mySqlBackup() { + File dirBackup = new File(dataFolder + File.separator + "backups"); - if (!dirBackup.exists()) + if (!dirBackup.exists()) { dirBackup.mkdir(); - if (checkWindows(Settings.backupWindowsPath)) { - String executeCmd = Settings.backupWindowsPath + "\\bin\\mysqldump.exe -u " + dbUserName + " -p" + dbPassword + " " + dbName + " --tables " + tblname + " -r " + path + ".sql"; + } + String backupWindowsPath = settings.getProperty(BackupSettings.MYSQL_WINDOWS_PATH); + if (checkWindows(backupWindowsPath)) { + String executeCmd = backupWindowsPath + "\\bin\\mysqldump.exe -u " + dbUserName + " -p" + dbPassword + " " + dbName + " --tables " + tblname + " -r " + path + ".sql"; Process runtimeProcess; try { runtimeProcess = Runtime.getRuntime().exec(executeCmd); @@ -102,8 +114,12 @@ public class PerformBackup { } else { ConsoleLogger.showError("Could not create the backup!"); } - } catch (Exception ex) { - ex.printStackTrace(); + } catch (IOException e) { + ConsoleLogger.showError("Error during backup: " + StringUtils.formatException(e)); + ConsoleLogger.writeStackTrace(e); + } catch (InterruptedException e) { + ConsoleLogger.showError("Backup was interrupted: " + StringUtils.formatException(e)); + ConsoleLogger.writeStackTrace(e); } } else { String executeCmd = "mysqldump -u " + dbUserName + " -p" + dbPassword + " " + dbName + " --tables " + tblname + " -r " + path + ".sql"; @@ -117,69 +133,54 @@ public class PerformBackup { } else { ConsoleLogger.showError("Could not create the backup!"); } - } catch (Exception ex) { - ex.printStackTrace(); + } catch (IOException e) { + ConsoleLogger.showError("Error during backup: " + StringUtils.formatException(e)); + ConsoleLogger.writeStackTrace(e); + } catch (InterruptedException e) { + ConsoleLogger.showError("Backup was interrupted: " + StringUtils.formatException(e)); + ConsoleLogger.writeStackTrace(e); } } return false; } - /** - * Method FileBackup. - * - * @param backend String - * - * @return boolean - */ - private boolean FileBackup(String backend) { - File dirBackup = new File(AuthMe.getInstance().getDataFolder() + "/backups"); + private boolean fileBackup(String backend) { + File dirBackup = new File(dataFolder + File.separator + "backups"); if (!dirBackup.exists()) dirBackup.mkdir(); try { - copy(new File("plugins" + File.separator + "AuthMe" + File.separator + backend), new File(path + ".db")); + copy("plugins" + File.separator + "AuthMe" + File.separator + backend, path + ".db"); return true; - - } catch (Exception ex) { - ex.printStackTrace(); + } catch (IOException ex) { + ConsoleLogger.showError("Encountered an error during file backup: " + StringUtils.formatException(ex)); + ConsoleLogger.writeStackTrace(ex); } return false; } /** - * Method checkWindows. + * Check if we are under Windows and correct location of mysqldump.exe + * otherwise return error. * - * @param windowsPath String - * - * @return boolean + * @param windowsPath The path to check + * @return True if the path is correct, false if it is incorrect or the OS is not Windows */ - private boolean checkWindows(String windowsPath) { + private static boolean checkWindows(String windowsPath) { String isWin = System.getProperty("os.name").toLowerCase(); if (isWin.contains("win")) { if (new File(windowsPath + "\\bin\\mysqldump.exe").exists()) { return true; } else { - ConsoleLogger.showError("Mysql Windows Path is incorrect please check it"); - return true; + ConsoleLogger.showError("Mysql Windows Path is incorrect. Please check it"); + return false; } - } else return false; + } + return false; } - /* - * Check if we are under Windows and correct location of mysqldump.exe - * otherwise return error. - */ - - /** - * Method copy. - * - * @param src File - * @param dst File - * - * @throws IOException - */ - void copy(File src, File dst) throws IOException { + private static void copy(String src, String dst) throws IOException { InputStream in = new FileInputStream(src); OutputStream out = new FileOutputStream(dst); @@ -193,27 +194,6 @@ public class PerformBackup { out.close(); } - /* - * Copyr src bytefile into dst file - */ - - /** - * Method getInstance. - * - * @return AuthMe - */ - public AuthMe getInstance() { - return instance; - } - - /** - * Method setInstance. - * - * @param instance AuthMe - */ - public void setInstance(AuthMe instance) { - this.instance = instance; - } /** * Possible backup causes. @@ -222,7 +202,7 @@ public class PerformBackup { START, STOP, COMMAND, - OTHER, + OTHER } } diff --git a/src/main/java/fr/xephi/authme/command/CommandMapper.java b/src/main/java/fr/xephi/authme/command/CommandMapper.java index 8bc5dccdd..e43ee4bb1 100644 --- a/src/main/java/fr/xephi/authme/command/CommandMapper.java +++ b/src/main/java/fr/xephi/authme/command/CommandMapper.java @@ -1,8 +1,6 @@ package fr.xephi.authme.command; import fr.xephi.authme.command.executable.HelpCommand; -import fr.xephi.authme.command.help.HelpProvider; -import fr.xephi.authme.output.Messages; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.util.CollectionUtils; import fr.xephi.authme.util.StringUtils; diff --git a/src/main/java/fr/xephi/authme/command/CommandService.java b/src/main/java/fr/xephi/authme/command/CommandService.java index 8dd1da599..5b8b5e490 100644 --- a/src/main/java/fr/xephi/authme/command/CommandService.java +++ b/src/main/java/fr/xephi/authme/command/CommandService.java @@ -8,10 +8,11 @@ import fr.xephi.authme.output.Messages; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.process.Management; import fr.xephi.authme.security.PasswordSecurity; -import fr.xephi.authme.settings.custom.NewSetting; +import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.domain.Property; import org.bukkit.command.CommandSender; +import java.io.File; import java.util.List; /** @@ -101,6 +102,16 @@ public class CommandService { return authMe.getDataSource(); } + /** + * Return the AuthMe instance for further manipulation. Use only if other methods from + * the command service cannot be used. + * + * @return The AuthMe instance + */ + public AuthMe getAuthMe() { + return authMe; + } + /** * Return the PasswordSecurity instance. * @@ -152,6 +163,15 @@ public class CommandService { return messages.retrieve(key); } + /** + * Change the messages instance to retrieve messages from the given file. + * + * @param file The new file to read messages from + */ + public void reloadMessages(File file) { + messages.reload(file); + } + /** * Retrieve the given property's value. * @@ -163,4 +183,13 @@ public class CommandService { return settings.getProperty(property); } + /** + * Return the settings manager. + * + * @return The settings manager + */ + public NewSetting getSettings() { + return settings; + } + } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java index a1b27f16f..cb6bddcf2 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/ChangePasswordAdminCommand.java @@ -8,8 +8,8 @@ import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.security.crypts.HashedPassword; -import fr.xephi.authme.settings.custom.RestrictionSettings; -import fr.xephi.authme.settings.custom.SecuritySettings; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.command.CommandSender; import java.util.List; diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java index 107f8c7ae..7b9299526 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/RegisterAdminCommand.java @@ -7,7 +7,7 @@ import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.security.crypts.HashedPassword; import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.settings.custom.SecuritySettings; +import fr.xephi.authme.settings.properties.SecuritySettings; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java index 7e4ad2b19..39acd6a19 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/ReloadCommand.java @@ -1,36 +1,31 @@ package fr.xephi.authme.command.executable.authme; -import java.util.List; - -import org.bukkit.command.CommandSender; - import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.output.MessageKey; -import fr.xephi.authme.output.Messages; -import fr.xephi.authme.settings.Settings; +import org.bukkit.command.CommandSender; +import java.util.List; + +/** + * The reload command. + */ public class ReloadCommand implements ExecutableCommand { @Override public void executeCommand(CommandSender sender, List arguments, CommandService commandService) { - // AuthMe plugin instance - AuthMe plugin = AuthMe.getInstance(); - + AuthMe plugin = commandService.getAuthMe(); try { - Settings.reload(); - Messages.getInstance().reloadManager(); - plugin.getModuleManager().reloadModules(); + commandService.getSettings().reload(); + commandService.reloadMessages(commandService.getSettings().getMessagesFile()); plugin.setupDatabase(); + commandService.send(sender, MessageKey.CONFIG_RELOAD_SUCCESS); } catch (Exception e) { sender.sendMessage("Error occurred during reload of AuthMe: aborting"); - ConsoleLogger.showError("Fatal error occurred! AuthMe instance ABORTED!"); - ConsoleLogger.writeStackTrace(e); + ConsoleLogger.logException("Aborting! Encountered exception during reload of AuthMe:", e); plugin.stopOrUnload(); } - - commandService.send(sender, MessageKey.CONFIG_RELOAD_SUCCESS); } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/SetEmailCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/SetEmailCommand.java index 57e3d08e5..ca794bb46 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/SetEmailCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/SetEmailCommand.java @@ -5,7 +5,7 @@ import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.output.MessageKey; -import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.util.Utils; import org.bukkit.command.CommandSender; import java.util.List; @@ -20,7 +20,7 @@ public class SetEmailCommand implements ExecutableCommand { final String playerEmail = arguments.get(1); // Validate the email address - if (!Settings.isEmailCorrect(playerEmail)) { + if (!Utils.isEmailCorrect(playerEmail, commandService.getSettings())) { commandService.send(sender, MessageKey.INVALID_EMAIL); return; } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/VersionCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/VersionCommand.java index 64a35dd32..bfbb2ebf6 100644 --- a/src/main/java/fr/xephi/authme/command/executable/authme/VersionCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/authme/VersionCommand.java @@ -3,7 +3,6 @@ package fr.xephi.authme.command.executable.authme; import fr.xephi.authme.AuthMe; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; -import fr.xephi.authme.settings.Settings; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; @@ -11,12 +10,15 @@ import org.bukkit.entity.Player; import java.util.List; +import static fr.xephi.authme.settings.properties.PluginSettings.HELP_HEADER; + public class VersionCommand implements ExecutableCommand { @Override public void executeCommand(CommandSender sender, List arguments, CommandService commandService) { // Show some version info - sender.sendMessage(ChatColor.GOLD + "==========[ " + Settings.helpHeader + " ABOUT ]=========="); + sender.sendMessage(ChatColor.GOLD + "==========[ " + commandService.getProperty(HELP_HEADER) + + " ABOUT ]=========="); sender.sendMessage(ChatColor.GOLD + "Version: " + ChatColor.WHITE + AuthMe.getPluginName() + " v" + AuthMe.getPluginVersion() + ChatColor.GRAY + " (build: " + AuthMe.getPluginBuildNumber() + ")"); sender.sendMessage(ChatColor.GOLD + "Developers:"); diff --git a/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java b/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java index 79f963f0b..8c5a0764a 100644 --- a/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/captcha/CaptchaCommand.java @@ -6,7 +6,7 @@ import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.security.RandomString; -import fr.xephi.authme.settings.custom.SecuritySettings; +import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.util.Wrapper; import org.bukkit.entity.Player; diff --git a/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java b/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java index a3b7f4451..d27ff2987 100644 --- a/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommand.java @@ -5,8 +5,8 @@ import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.output.MessageKey; -import fr.xephi.authme.settings.custom.RestrictionSettings; -import fr.xephi.authme.settings.custom.SecuritySettings; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.task.ChangePasswordTask; import fr.xephi.authme.util.Wrapper; import org.bukkit.entity.Player; diff --git a/src/main/java/fr/xephi/authme/command/executable/email/AddEmailCommand.java b/src/main/java/fr/xephi/authme/command/executable/email/AddEmailCommand.java index 663372cda..f2b66301b 100644 --- a/src/main/java/fr/xephi/authme/command/executable/email/AddEmailCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/email/AddEmailCommand.java @@ -3,8 +3,7 @@ package fr.xephi.authme.command.executable.email; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.PlayerCommand; import fr.xephi.authme.output.MessageKey; -import fr.xephi.authme.settings.Settings; -import fr.xephi.authme.util.StringUtils; +import fr.xephi.authme.util.Utils; import org.bukkit.entity.Player; import java.util.List; @@ -16,7 +15,7 @@ public class AddEmailCommand extends PlayerCommand { String email = arguments.get(0); String emailConfirmation = arguments.get(1); - if (StringUtils.isEmpty(email) || "your@email.com".equals(email) || !Settings.isEmailCorrect(email)) { + if (!Utils.isEmailCorrect(email, commandService.getSettings())) { commandService.send(player, MessageKey.INVALID_EMAIL); } else if (email.equals(emailConfirmation)) { commandService.getManagement().performAddEmail(player, email); diff --git a/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java b/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java index 8ba9f2cb7..175d067c0 100644 --- a/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/register/RegisterCommand.java @@ -6,6 +6,7 @@ import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.process.Management; import fr.xephi.authme.security.RandomString; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.util.Utils; import org.bukkit.entity.Player; import java.util.List; @@ -26,7 +27,7 @@ public class RegisterCommand extends PlayerCommand { return; } final String email = arguments.get(0); - if (!Settings.isEmailCorrect(email)) { + if (!Utils.isEmailCorrect(email, commandService.getSettings())) { commandService.send(player, MessageKey.INVALID_EMAIL); return; } diff --git a/src/main/java/fr/xephi/authme/command/help/HelpProvider.java b/src/main/java/fr/xephi/authme/command/help/HelpProvider.java index f05d21171..7bc06f03d 100644 --- a/src/main/java/fr/xephi/authme/command/help/HelpProvider.java +++ b/src/main/java/fr/xephi/authme/command/help/HelpProvider.java @@ -10,7 +10,6 @@ import fr.xephi.authme.command.FoundCommandResult; import fr.xephi.authme.permission.DefaultPermission; import fr.xephi.authme.permission.PermissionNode; import fr.xephi.authme.permission.PermissionsManager; -import fr.xephi.authme.settings.Settings; import fr.xephi.authme.util.CollectionUtils; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; @@ -44,9 +43,11 @@ public class HelpProvider { public static final int ALL_OPTIONS = ~HIDE_COMMAND; private final PermissionsManager permissionsManager; + private final String helpHeader; - public HelpProvider(PermissionsManager permissionsManager) { + public HelpProvider(PermissionsManager permissionsManager, String helpHeader) { this.permissionsManager = permissionsManager; + this.helpHeader = helpHeader; } public List printHelp(CommandSender sender, FoundCommandResult result, int options) { @@ -55,7 +56,7 @@ public class HelpProvider { } List lines = new ArrayList<>(); - lines.add(ChatColor.GOLD + "==========[ " + Settings.helpHeader + " HELP ]=========="); + lines.add(ChatColor.GOLD + "==========[ " + helpHeader + " HELP ]=========="); CommandDescription command = result.getCommandDescription(); List labels = ImmutableList.copyOf(result.getLabels()); diff --git a/src/main/java/fr/xephi/authme/converter/ForceFlatToSqlite.java b/src/main/java/fr/xephi/authme/converter/ForceFlatToSqlite.java index b0ea61c0b..d873aa506 100644 --- a/src/main/java/fr/xephi/authme/converter/ForceFlatToSqlite.java +++ b/src/main/java/fr/xephi/authme/converter/ForceFlatToSqlite.java @@ -4,32 +4,41 @@ import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.SQLite; -import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.DatabaseSettings; +import fr.xephi.authme.util.StringUtils; + +import java.sql.SQLException; /** + * Mandatory migration from the deprecated flat file datasource to SQLite. */ public class ForceFlatToSqlite { - private final DataSource data; + private final DataSource database; + private final NewSetting settings; - public ForceFlatToSqlite(DataSource data) { - this.data = data; + public ForceFlatToSqlite(DataSource database, NewSetting settings) { + this.database = database; + this.settings = settings; } public DataSource run() { - DataSource sqlite = null; try { - sqlite = new SQLite(); - for (PlayerAuth auth : data.getAllAuths()) { + DataSource sqlite = new SQLite(); + for (PlayerAuth auth : database.getAllAuths()) { auth.setRealName("Player"); sqlite.saveAuth(auth); } - Settings.setValue("DataSource.backend", "sqlite"); - ConsoleLogger.info("Database successfully converted to sqlite !"); - } catch (Exception e) { - ConsoleLogger.showError("An error occurred while trying to convert flatfile to sqlite ..."); - return null; + settings.setProperty(DatabaseSettings.BACKEND, DataSource.DataSourceType.SQLITE); + settings.save(); + ConsoleLogger.info("Database successfully converted to sqlite!"); + return sqlite; + } catch (SQLException | ClassNotFoundException e) { + ConsoleLogger.showError("An error occurred while trying to convert flatfile to sqlite: " + + StringUtils.formatException(e)); + ConsoleLogger.writeStackTrace(e); } - return sqlite; + return null; } } diff --git a/src/main/java/fr/xephi/authme/converter/RoyalAuthConverter.java b/src/main/java/fr/xephi/authme/converter/RoyalAuthConverter.java index 5e85ce6ed..42bd681df 100644 --- a/src/main/java/fr/xephi/authme/converter/RoyalAuthConverter.java +++ b/src/main/java/fr/xephi/authme/converter/RoyalAuthConverter.java @@ -33,8 +33,7 @@ public class RoyalAuthConverter implements Converter { PlayerAuth auth = new PlayerAuth(name, ra.getHash(), "127.0.0.1", ra.getLastLogin(), "your@email.com", o.getName()); data.saveAuth(auth); } catch (Exception e) { - ConsoleLogger.writeStackTrace(e); - ConsoleLogger.showError("Error while trying to import " + o.getName() + " RoyalAuth datas"); + ConsoleLogger.logException("Error while trying to import " + o.getName() + " RoyalAuth data", e); } } } diff --git a/src/main/java/fr/xephi/authme/converter/vAuthFileReader.java b/src/main/java/fr/xephi/authme/converter/vAuthFileReader.java index 16b89e5ac..bb25b04bd 100644 --- a/src/main/java/fr/xephi/authme/converter/vAuthFileReader.java +++ b/src/main/java/fr/xephi/authme/converter/vAuthFileReader.java @@ -8,6 +8,7 @@ import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import java.io.File; +import java.io.IOException; import java.util.Scanner; import java.util.UUID; @@ -27,7 +28,7 @@ class vAuthFileReader { } public void convert() { - final File file = new File(plugin.getDataFolder().getParent() + "" + File.separator + "vAuth" + File.separator + "passwords.yml"); + final File file = new File(plugin.getDataFolder().getParent() + File.separator + "vAuth" + File.separator + "passwords.yml"); Scanner scanner; try { scanner = new Scanner(file); @@ -52,8 +53,8 @@ class vAuthFileReader { database.saveAuth(auth); } scanner.close(); - } catch (Exception e) { - ConsoleLogger.writeStackTrace(e); + } catch (IOException e) { + ConsoleLogger.logException("Error while trying to import some vAuth data", e); } } @@ -63,12 +64,10 @@ class vAuthFileReader { } private String getName(UUID uuid) { - try { - for (OfflinePlayer op : Bukkit.getOfflinePlayers()) { - if (op.getUniqueId().compareTo(uuid) == 0) - return op.getName(); + for (OfflinePlayer op : Bukkit.getOfflinePlayers()) { + if (op.getUniqueId().compareTo(uuid) == 0) { + return op.getName(); } - } catch (Exception ignored) { } return null; } diff --git a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java index 6fe3f2267..2d7dc76f8 100644 --- a/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java +++ b/src/main/java/fr/xephi/authme/datasource/CacheDataSource.java @@ -5,17 +5,13 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.cache.RemovalListener; -import com.google.common.cache.RemovalListeners; import com.google.common.cache.RemovalNotification; -import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.cache.auth.PlayerAuth; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.security.crypts.HashedPassword; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** @@ -23,7 +19,6 @@ import java.util.concurrent.TimeUnit; public class CacheDataSource implements DataSource { private final DataSource source; - private final ExecutorService exec; private final LoadingCache> cachedAuths; /** @@ -33,10 +28,9 @@ public class CacheDataSource implements DataSource { */ public CacheDataSource(DataSource src) { this.source = src; - this.exec = Executors.newCachedThreadPool(); - cachedAuths = CacheBuilder.newBuilder() - .expireAfterWrite(5, TimeUnit.MINUTES) - .removalListener(RemovalListeners.asynchronous(new RemovalListener>() { + this.cachedAuths = CacheBuilder.newBuilder() + .expireAfterWrite(8, TimeUnit.MINUTES) + .removalListener(new RemovalListener>() { @Override public void onRemoval(RemovalNotification> removalNotification) { String name = removalNotification.getKey(); @@ -44,7 +38,7 @@ public class CacheDataSource implements DataSource { cachedAuths.getUnchecked(name); } } - }, exec)) + }) .build( new CacheLoader>() { public Optional load(String key) { @@ -168,24 +162,13 @@ public class CacheDataSource implements DataSource { @Override public synchronized void close() { - try { - exec.shutdown(); - exec.awaitTermination(8, TimeUnit.SECONDS); - } catch (InterruptedException e) { - ConsoleLogger.writeStackTrace(e); - } source.close(); } @Override public void reload() { // unused method - exec.execute(new Runnable() { - @Override - public void run() { - source.reload(); - cachedAuths.invalidateAll(); - } - }); + source.reload(); + cachedAuths.invalidateAll(); } @Override @@ -214,13 +197,8 @@ public class CacheDataSource implements DataSource { @Override public synchronized void purgeBanned(final List banned) { - exec.execute(new Runnable() { - @Override - public void run() { - source.purgeBanned(banned); - cachedAuths.invalidateAll(banned); - } - }); + source.purgeBanned(banned); + cachedAuths.invalidateAll(banned); } @Override @@ -235,33 +213,18 @@ public class CacheDataSource implements DataSource { @Override public void setLogged(final String user) { - exec.execute(new Runnable() { - @Override - public void run() { - source.setLogged(user.toLowerCase()); - } - }); + source.setLogged(user.toLowerCase()); } @Override public void setUnlogged(final String user) { - exec.execute(new Runnable() { - @Override - public void run() { - source.setUnlogged(user.toLowerCase()); - } - }); + source.setUnlogged(user.toLowerCase()); } @Override public void purgeLogged() { - exec.execute(new Runnable() { - @Override - public void run() { - source.purgeLogged(); - cachedAuths.invalidateAll(); - } - }); + source.purgeLogged(); + cachedAuths.invalidateAll(); } @Override @@ -270,14 +233,9 @@ public class CacheDataSource implements DataSource { } @Override - public void updateName(final String oldOne, final String newOne) { - exec.execute(new Runnable() { - @Override - public void run() { - source.updateName(oldOne, newOne); - cachedAuths.invalidate(oldOne); - } - }); + public void updateName(final String oldOne, final String newOne) { // unused method + source.updateName(oldOne, newOne); + cachedAuths.invalidate(oldOne); } @Override diff --git a/src/main/java/fr/xephi/authme/datasource/FlatFile.java b/src/main/java/fr/xephi/authme/datasource/FlatFile.java index fd93047c0..eca0bbc5c 100644 --- a/src/main/java/fr/xephi/authme/datasource/FlatFile.java +++ b/src/main/java/fr/xephi/authme/datasource/FlatFile.java @@ -36,17 +36,19 @@ public class FlatFile implements DataSource { private final File source; public FlatFile() { - source = Settings.AUTH_FILE; + AuthMe instance = AuthMe.getInstance(); + + source = new File(instance.getDataFolder(), "auths.db"); try { source.createNewFile(); } catch (IOException e) { ConsoleLogger.showError(e.getMessage()); if (Settings.isStopEnabled) { ConsoleLogger.showError("Can't use FLAT FILE... SHUTDOWN..."); - AuthMe.getInstance().getServer().shutdown(); + instance.getServer().shutdown(); } if (!Settings.isStopEnabled) { - AuthMe.getInstance().getServer().getPluginManager().disablePlugin(AuthMe.getInstance()); + instance.getServer().getPluginManager().disablePlugin(instance); } e.printStackTrace(); } diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index 6ab9a39d0..a97974fb8 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -257,8 +257,7 @@ public class MySQL implements DataSource { ResultSet rs = pst.executeQuery(); return rs.next(); } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); } return false; } @@ -276,8 +275,7 @@ public class MySQL implements DataSource { !columnSalt.isEmpty() ? rs.getString(columnSalt) : null); } } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); } return null; } @@ -322,8 +320,7 @@ public class MySQL implements DataSource { } } } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); return null; } return pAuth; @@ -525,8 +522,7 @@ public class MySQL implements DataSource { } return true; } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); } return false; } @@ -589,8 +585,7 @@ public class MySQL implements DataSource { } return true; } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); } return false; } @@ -623,8 +618,7 @@ public class MySQL implements DataSource { pst.setLong(1, until); result = pst.executeUpdate(); } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); } return result; } @@ -647,8 +641,7 @@ public class MySQL implements DataSource { st.executeUpdate(); st.close(); } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); } return list; } @@ -680,8 +673,7 @@ public class MySQL implements DataSource { pst.executeUpdate(); return true; } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); } return false; } @@ -702,8 +694,7 @@ public class MySQL implements DataSource { pst.close(); return true; } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); } return false; } @@ -722,8 +713,7 @@ public class MySQL implements DataSource { rs.close(); pst.close(); } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); } return countIp; } @@ -739,8 +729,7 @@ public class MySQL implements DataSource { pst.close(); return true; } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); } return false; } @@ -750,9 +739,8 @@ public class MySQL implements DataSource { try { reloadArguments(); } catch (Exception ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.showError("Can't reconnect to MySQL database... Please check your MySQL configuration!"); - ConsoleLogger.writeStackTrace(ex); + ConsoleLogger.logException("Can't reconnect to MySQL database... " + + "Please check your MySQL configuration! Encountered", ex); AuthMe.getInstance().stopOrUnload(); } } @@ -778,8 +766,7 @@ public class MySQL implements DataSource { rs.close(); pst.close(); } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); } return result; } @@ -798,8 +785,7 @@ public class MySQL implements DataSource { rs.close(); pst.close(); } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); } return result; } @@ -818,8 +804,7 @@ public class MySQL implements DataSource { rs.close(); pst.close(); } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); } return countEmail; } @@ -834,8 +819,7 @@ public class MySQL implements DataSource { } pst.close(); } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); } } @@ -854,8 +838,7 @@ public class MySQL implements DataSource { ResultSet rs = pst.executeQuery(); isLogged = rs.next() && (rs.getInt(columnLogged) == 1); } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); } return isLogged; } @@ -870,8 +853,7 @@ public class MySQL implements DataSource { pst.executeUpdate(); pst.close(); } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); } } @@ -885,8 +867,7 @@ public class MySQL implements DataSource { pst.executeUpdate(); pst.close(); } catch (SQLException ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + logSqlException(ex); } } @@ -899,9 +880,8 @@ public class MySQL implements DataSource { pst.setInt(2, 1); pst.executeUpdate(); pst.close(); - } catch (Exception ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + } catch (SQLException ex) { + logSqlException(ex); } } @@ -916,9 +896,8 @@ public class MySQL implements DataSource { } rs.close(); st.close(); - } catch (Exception ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + } catch (SQLException ex) { + logSqlException(ex); } return result; } @@ -931,9 +910,8 @@ public class MySQL implements DataSource { pst.setString(1, newOne); pst.setString(2, oldOne); pst.executeUpdate(); - } catch (Exception ex) { - ConsoleLogger.showError(ex.getMessage()); - ConsoleLogger.writeStackTrace(ex); + } catch (SQLException ex) { + logSqlException(ex); } } @@ -1041,8 +1019,7 @@ public class MySQL implements DataSource { } private static void logSqlException(SQLException e) { - ConsoleLogger.showError("Error executing SQL query: " + StringUtils.formatException(e)); - ConsoleLogger.writeStackTrace(e); + ConsoleLogger.logException("Error during SQL operation:", e); } } diff --git a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java b/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java index f672250c7..bae29ab74 100644 --- a/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java +++ b/src/main/java/fr/xephi/authme/listener/AuthMePlayerListener.java @@ -108,6 +108,9 @@ public class AuthMePlayerListener implements Listener { if (Settings.useEssentialsMotd && cmd.equals("/motd")) { return; } + if(!Settings.isForcedRegistrationEnabled && Settings.allowAllCommandsIfRegIsOptional) { + return; + } if (Settings.allowCommands.contains(cmd)) { return; } @@ -185,7 +188,7 @@ public class AuthMePlayerListener implements Listener { } } - @EventHandler(priority = EventPriority.LOW) + @EventHandler(priority = EventPriority.LOWEST) public void onPlayerJoin(PlayerJoinEvent event) { final Player player = event.getPlayer(); if (player == null) { diff --git a/src/main/java/fr/xephi/authme/mail/SendMailSSL.java b/src/main/java/fr/xephi/authme/mail/SendMailSSL.java index 692497d0d..8279b8dfa 100644 --- a/src/main/java/fr/xephi/authme/mail/SendMailSSL.java +++ b/src/main/java/fr/xephi/authme/mail/SendMailSSL.java @@ -1,25 +1,26 @@ package fr.xephi.authme.mail; -import java.io.File; -import java.io.IOException; -import java.security.Security; -import java.util.Properties; - -import javax.activation.DataSource; -import javax.activation.FileDataSource; -import javax.imageio.ImageIO; -import javax.mail.Session; - -import org.apache.commons.mail.EmailException; -import org.apache.commons.mail.HtmlEmail; -import org.bukkit.Bukkit; - import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.ImageGenerator; import fr.xephi.authme.cache.auth.PlayerAuth; -import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.EmailSettings; import fr.xephi.authme.util.StringUtils; +import org.apache.commons.mail.EmailConstants; +import org.apache.commons.mail.EmailException; +import org.apache.commons.mail.HtmlEmail; +import org.bukkit.Bukkit; + +import javax.activation.DataSource; +import javax.activation.FileDataSource; +import javax.imageio.ImageIO; +import javax.mail.Session; +import java.io.File; +import java.io.IOException; +import java.security.Security; +import java.util.Properties; + /** * @author Xephi59 @@ -27,13 +28,15 @@ import fr.xephi.authme.util.StringUtils; public class SendMailSSL { private final AuthMe plugin; + private final NewSetting settings; - public SendMailSSL(AuthMe plugin) { + public SendMailSSL(AuthMe plugin, NewSetting settings) { this.plugin = plugin; + this.settings = settings; } public void main(final PlayerAuth auth, final String newPass) { - final String mailText = replaceMailTags(Settings.getMailText, plugin, auth, newPass); + final String mailText = replaceMailTags(settings.getEmailMessage(), plugin, auth, newPass); Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() { @Override @@ -41,21 +44,23 @@ public class SendMailSSL { Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); HtmlEmail email; try { - email = initializeMail(auth); + email = initializeMail(auth, settings); } catch (EmailException e) { - ConsoleLogger.showError("Failed to create email with the given settings: " + StringUtils.formatException(e)); + ConsoleLogger.showError("Failed to create email with the given settings: " + + StringUtils.formatException(e)); return; } String content = mailText; // Generate an image? File file = null; - if (Settings.generateImage) { + if (settings.getProperty(EmailSettings.PASSWORD_AS_IMAGE)) { try { file = generateImage(auth, plugin, newPass); content = embedImageIntoEmailContent(file, email, content); } catch (IOException | EmailException e) { - ConsoleLogger.showError("Unable to send new password as image for email " + auth.getEmail() + ": " + StringUtils.formatException(e)); + ConsoleLogger.showError("Unable to send new password as image for email " + auth.getEmail() + + ": " + StringUtils.formatException(e)); } } @@ -68,43 +73,39 @@ public class SendMailSSL { }); } - private static File generateImage(PlayerAuth auth, AuthMe plugin, - String newPass) throws IOException { + private static File generateImage(PlayerAuth auth, AuthMe plugin, String newPass) throws IOException { ImageGenerator gen = new ImageGenerator(newPass); - File file = new File(plugin.getDataFolder() + File.separator + auth.getNickname() + "_new_pass.jpg"); + File file = new File(plugin.getDataFolder(), auth.getNickname() + "_new_pass.jpg"); ImageIO.write(gen.generateImage(), "jpg", file); return file; } - private static String embedImageIntoEmailContent(File image, - HtmlEmail email, String content) throws EmailException { + private static String embedImageIntoEmailContent(File image, HtmlEmail email, String content) + throws EmailException { DataSource source = new FileDataSource(image); String tag = email.embed(source, image.getName()); return content.replace("", ""); } - private static HtmlEmail initializeMail(PlayerAuth auth) + private static HtmlEmail initializeMail(PlayerAuth auth, NewSetting settings) throws EmailException { - String senderName; - if (StringUtils.isEmpty(Settings.getmailSenderName)) { - senderName = Settings.getmailAccount; - } else { - senderName = Settings.getmailSenderName; - } - String senderMail = Settings.getmailAccount; - String mailPassword = Settings.getmailPassword; - int port = Settings.getMailPort; + String senderMail = settings.getProperty(EmailSettings.MAIL_ACCOUNT); + String senderName = StringUtils.isEmpty(settings.getProperty(EmailSettings.MAIL_SENDER_NAME)) + ? senderMail + : settings.getProperty(EmailSettings.MAIL_SENDER_NAME); + String mailPassword = settings.getProperty(EmailSettings.MAIL_PASSWORD); + int port = settings.getProperty(EmailSettings.SMTP_PORT); HtmlEmail email = new HtmlEmail(); - email.setCharset(org.apache.commons.mail.EmailConstants.UTF_8); + email.setCharset(EmailConstants.UTF_8); email.setSmtpPort(port); - email.setHostName(Settings.getmailSMTP); + email.setHostName(settings.getProperty(EmailSettings.SMTP_HOST)); email.addTo(auth.getEmail()); email.setFrom(senderMail, senderName); - email.setSubject(Settings.getMailSubject); + email.setSubject(settings.getProperty(EmailSettings.RECOVERY_MAIL_SUBJECT)); email.setAuthentication(senderMail, mailPassword); - setPropertiesForPort(email, port); + setPropertiesForPort(email, port, settings); return email; } @@ -113,7 +114,8 @@ public class SendMailSSL { email.setHtmlMsg(content); email.setTextMsg(content); } catch (EmailException e) { - ConsoleLogger.showError("Your email.html config contains an error and cannot be sent: " + StringUtils.formatException(e)); + ConsoleLogger.showError("Your email.html config contains an error and cannot be sent: " + + StringUtils.formatException(e)); return false; } try { @@ -125,17 +127,19 @@ public class SendMailSSL { } } - private static String replaceMailTags(String mailText, AuthMe plugin, - PlayerAuth auth, String newPass) { - return mailText.replace("", auth.getNickname()).replace("", plugin.getServer().getServerName()).replace("", newPass); + private static String replaceMailTags(String mailText, AuthMe plugin, PlayerAuth auth, String newPass) { + return mailText + .replace("", auth.getNickname()) + .replace("", plugin.getServer().getServerName()) + .replace("", newPass); } - @SuppressWarnings("deprecation") - private static void setPropertiesForPort(HtmlEmail email, int port) + private static void setPropertiesForPort(HtmlEmail email, int port, NewSetting settings) throws EmailException { switch (port) { case 587: - if (!Settings.emailOauth2Token.isEmpty()) { + String oAuth2Token = settings.getProperty(EmailSettings.OAUTH2_TOKEN); + if (!oAuth2Token.isEmpty()) { if (Security.getProvider("Google OAuth2 Provider") == null) { Security.addProvider(new OAuth2Provider()); } @@ -146,7 +150,7 @@ public class SendMailSSL { mailProperties.setProperty("mail.smtp.sasl.mechanisms", "XOAUTH2"); mailProperties.setProperty("mail.smtp.auth.login.disable", "true"); mailProperties.setProperty("mail.smtp.auth.plain.disable", "true"); - mailProperties.setProperty(OAuth2SaslClientFactory.OAUTH_TOKEN_PROP, Settings.emailOauth2Token); + mailProperties.setProperty(OAuth2SaslClientFactory.OAUTH_TOKEN_PROP, oAuth2Token); email.setMailSession(Session.getInstance(mailProperties)); } else { email.setStartTLSEnabled(true); @@ -159,7 +163,7 @@ public class SendMailSSL { email.setSSLCheckServerIdentity(true); break; case 465: - email.setSslSmtpPort("" + port); + email.setSslSmtpPort(Integer.toString(port)); email.setSSL(true); break; default: diff --git a/src/main/java/fr/xephi/authme/modules/ModuleManager.java b/src/main/java/fr/xephi/authme/modules/ModuleManager.java index 23aa37a88..6228091c2 100644 --- a/src/main/java/fr/xephi/authme/modules/ModuleManager.java +++ b/src/main/java/fr/xephi/authme/modules/ModuleManager.java @@ -135,8 +135,7 @@ public class ModuleManager { } } catch (Exception ex) { - ConsoleLogger.writeStackTrace(ex); - ConsoleLogger.showError("Cannot load " + pathToJar.getName() + " jar file !"); + ConsoleLogger.logException("Cannot load " + pathToJar.getName() + " jar file!", ex); } finally { try { if (jarFile != null) { diff --git a/src/main/java/fr/xephi/authme/output/Messages.java b/src/main/java/fr/xephi/authme/output/Messages.java index e27e90618..731844d07 100644 --- a/src/main/java/fr/xephi/authme/output/Messages.java +++ b/src/main/java/fr/xephi/authme/output/Messages.java @@ -1,9 +1,10 @@ package fr.xephi.authme.output; -import fr.xephi.authme.settings.Settings; import fr.xephi.authme.util.StringUtils; import org.bukkit.command.CommandSender; +import java.io.File; + /** * Class for retrieving and sending translatable messages to players. * This class detects when the language settings have changed and will @@ -11,27 +12,15 @@ import org.bukkit.command.CommandSender; */ public class Messages { - private static Messages singleton; - private final String language; private MessagesManager manager; - - private Messages(String language, MessagesManager manager) { - this.language = language; - this.manager = manager; - } - /** - * Get the instance of Messages. + * Constructor. * - * @return The Messages instance + * @param messageFile The messages file to use */ - public static Messages getInstance() { - if (singleton == null) { - MessagesManager manager = new MessagesManager(Settings.messageFile); - singleton = new Messages(Settings.messagesLanguage, manager); - } - return singleton; + public Messages(File messageFile) { + manager = new MessagesManager(messageFile); } /** @@ -60,7 +49,8 @@ public class Messages { String message = retrieveSingle(key); String[] tags = key.getTags(); if (replacements.length != tags.length) { - throw new RuntimeException("Given replacement size does not match the tags in message key '" + key + "'"); + throw new IllegalStateException( + "Given replacement size does not match the tags in message key '" + key + "'"); } for (int i = 0; i < tags.length; ++i) { @@ -80,9 +70,6 @@ public class Messages { * @return The message split by new lines */ public String[] retrieve(MessageKey key) { - if (!Settings.messagesLanguage.equalsIgnoreCase(language)) { - reloadManager(); - } return manager.retrieve(key.getKey()); } @@ -100,8 +87,8 @@ public class Messages { /** * Reload the messages manager. */ - public void reloadManager() { - manager = new MessagesManager(Settings.messageFile); + public void reload(File messagesFile) { + manager = new MessagesManager(messagesFile); } } diff --git a/src/main/java/fr/xephi/authme/output/MessagesManager.java b/src/main/java/fr/xephi/authme/output/MessagesManager.java index 1308712a0..a280ea3ae 100644 --- a/src/main/java/fr/xephi/authme/output/MessagesManager.java +++ b/src/main/java/fr/xephi/authme/output/MessagesManager.java @@ -1,8 +1,8 @@ package fr.xephi.authme.output; import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.settings.CustomConfiguration; import org.bukkit.ChatColor; +import org.bukkit.configuration.file.YamlConfiguration; import java.io.File; @@ -12,7 +12,10 @@ import java.io.File; * This class is used within {@link Messages}, which offers a high-level interface for accessing * or sending messages from a properties file. */ -class MessagesManager extends CustomConfiguration { +class MessagesManager { + + private final YamlConfiguration configuration; + private final String fileName; /** * Constructor for Messages. @@ -20,8 +23,8 @@ class MessagesManager extends CustomConfiguration { * @param file the configuration file */ MessagesManager(File file) { - super(file); - load(); + this.fileName = file.getName(); + this.configuration = YamlConfiguration.loadConfiguration(file); } /** @@ -31,24 +34,22 @@ class MessagesManager extends CustomConfiguration { * * @return The message */ - String[] retrieve(String key) { - String message = (String) get(key); + public String[] retrieve(String key) { + String message = configuration.getString(key); if (message != null) { return formatMessage(message); } // Message is null: log key not being found and send error back as message String retrievalError = "Error getting message with key '" + key + "'. "; - ConsoleLogger.showError(retrievalError + "Please verify your config file at '" - + getConfigFile().getName() + "'"); + ConsoleLogger.showError(retrievalError + "Please verify your config file at '" + fileName + "'"); return new String[]{ retrievalError + "Please contact the admin to verify or update the AuthMe messages file."}; } - static String[] formatMessage(String message) { + private static String[] formatMessage(String message) { String[] lines = message.split("&n"); for (int i = 0; i < lines.length; ++i) { - // We don't initialize a StringBuilder here because mostly we will only have one entry lines[i] = ChatColor.translateAlternateColorCodes('&', lines[i]); } return lines; diff --git a/src/main/java/fr/xephi/authme/process/Management.java b/src/main/java/fr/xephi/authme/process/Management.java index 374b9b911..ec48328fb 100644 --- a/src/main/java/fr/xephi/authme/process/Management.java +++ b/src/main/java/fr/xephi/authme/process/Management.java @@ -2,7 +2,6 @@ package fr.xephi.authme.process; import fr.xephi.authme.AuthMe; import fr.xephi.authme.cache.auth.PlayerCache; -import fr.xephi.authme.process.email.AsyncAddEmail; import fr.xephi.authme.process.email.AsyncChangeEmail; import fr.xephi.authme.process.join.AsynchronousJoin; import fr.xephi.authme.process.login.AsynchronousLogin; @@ -10,6 +9,7 @@ import fr.xephi.authme.process.logout.AsynchronousLogout; import fr.xephi.authme.process.quit.AsynchronousQuit; import fr.xephi.authme.process.register.AsyncRegister; import fr.xephi.authme.process.unregister.AsynchronousUnregister; +import fr.xephi.authme.settings.NewSetting; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitScheduler; @@ -19,15 +19,17 @@ public class Management { private final AuthMe plugin; private final BukkitScheduler sched; + private final NewSetting settings; /** * Constructor for Management. * * @param plugin AuthMe */ - public Management(AuthMe plugin) { + public Management(AuthMe plugin, NewSetting settings) { this.plugin = plugin; this.sched = this.plugin.getServer().getScheduler(); + this.settings = settings; } public void performLogin(final Player player, final String password, final boolean forceLogin) { @@ -35,7 +37,8 @@ public class Management { @Override public void run() { - new AsynchronousLogin(player, password, forceLogin, plugin, plugin.getDataSource()).process(); + new AsynchronousLogin(player, password, forceLogin, plugin, plugin.getDataSource(), settings) + .process(); } }); } @@ -55,7 +58,7 @@ public class Management { @Override public void run() { - new AsyncRegister(player, password, email, plugin, plugin.getDataSource()).process(); + new AsyncRegister(player, password, email, plugin, plugin.getDataSource(), settings).process(); } }); } @@ -96,8 +99,7 @@ public class Management { sched.runTaskAsynchronously(plugin, new Runnable() { @Override public void run() { - new AsyncAddEmail(plugin, player, newEmail, plugin.getDataSource(), PlayerCache.getInstance()) - .process(); + new AsyncChangeEmail(player, plugin, null, newEmail, plugin.getDataSource(), PlayerCache.getInstance(), settings).process(); } }); } @@ -106,7 +108,7 @@ public class Management { sched.runTaskAsynchronously(plugin, new Runnable() { @Override public void run() { - new AsyncChangeEmail(player, plugin, oldEmail, newEmail, plugin.getDataSource(), PlayerCache.getInstance()).process(); + new AsyncChangeEmail(player, plugin, oldEmail, newEmail, plugin.getDataSource(), PlayerCache.getInstance(), settings).process(); } }); } diff --git a/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java b/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java index f98817d3f..5f0ef8fc7 100644 --- a/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java +++ b/src/main/java/fr/xephi/authme/process/email/AsyncAddEmail.java @@ -6,8 +6,10 @@ import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; +import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.util.StringUtils; +import fr.xephi.authme.util.Utils; import org.bukkit.entity.Player; /** @@ -20,13 +22,16 @@ public class AsyncAddEmail { private final Messages messages; private final DataSource dataSource; private final PlayerCache playerCache; + private final NewSetting settings; - public AsyncAddEmail(AuthMe plugin, Player player, String email, DataSource dataSource, PlayerCache playerCache) { + public AsyncAddEmail(AuthMe plugin, Player player, String email, DataSource dataSource, + PlayerCache playerCache, NewSetting settings) { this.messages = plugin.getMessages(); this.player = player; this.email = email; this.dataSource = dataSource; this.playerCache = playerCache; + this.settings = settings; } public void process() { @@ -52,9 +57,9 @@ public class AsyncAddEmail { } } - private static boolean isEmailInvalid(String email) { + private boolean isEmailInvalid(String email) { return StringUtils.isEmpty(email) || "your@email.com".equals(email) - || !Settings.isEmailCorrect(email); + || !Utils.isEmailCorrect(email, settings); } private void sendUnloggedMessage(DataSource dataSource) { diff --git a/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java b/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java index a17c262d9..791802fc1 100644 --- a/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java +++ b/src/main/java/fr/xephi/authme/process/email/AsyncChangeEmail.java @@ -6,8 +6,10 @@ import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; +import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.util.StringUtils; +import fr.xephi.authme.util.Utils; import org.bukkit.entity.Player; /** @@ -19,17 +21,19 @@ public class AsyncChangeEmail { private final String oldEmail; private final String newEmail; private final Messages m; + private final NewSetting settings; private final PlayerCache playerCache; private final DataSource dataSource; - public AsyncChangeEmail(Player player, AuthMe plugin, String oldEmail, - String newEmail, DataSource dataSource, PlayerCache playerCache) { + public AsyncChangeEmail(Player player, AuthMe plugin, String oldEmail, String newEmail, DataSource dataSource, + PlayerCache playerCache, NewSetting settings) { this.m = plugin.getMessages(); this.player = player; this.oldEmail = oldEmail; this.newEmail = newEmail; this.playerCache = playerCache; this.dataSource = dataSource; + this.settings = settings; } public void process() { @@ -54,9 +58,9 @@ public class AsyncChangeEmail { } } - private static boolean isEmailInvalid(String email) { + private boolean isEmailInvalid(String email) { return StringUtils.isEmpty(email) || "your@email.com".equals(email) - || !Settings.isEmailCorrect(email); + || !Utils.isEmailCorrect(email, settings); } private void saveNewEmail(PlayerAuth auth) { diff --git a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java index cbdac41eb..040c98fb3 100644 --- a/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java +++ b/src/main/java/fr/xephi/authme/process/join/AsynchronousJoin.java @@ -49,14 +49,14 @@ public class AsynchronousJoin { } public void process() { - if (Settings.checkVeryGames) { - plugin.getVerygamesIp(player); - } - if (Utils.isUnrestricted(player)) { return; } + if (Settings.checkVeryGames) { + plugin.getVerygamesIp(player); + } + if (plugin.ess != null && Settings.disableSocialSpy) { plugin.ess.getUser(player).setSocialSpyEnabled(false); } @@ -64,13 +64,13 @@ public class AsynchronousJoin { final String ip = plugin.getIP(player); - if (Settings.isAllowRestrictedIp && !Settings.getRestrictedIp(name, ip, player.getAddress().getHostName())) { + if (Settings.isAllowRestrictedIp && !isNameRestricted(name, ip, player.getAddress().getHostName())) { sched.scheduleSyncDelayedTask(plugin, new Runnable() { @Override public void run() { AuthMePlayerListener.causeByAuthMe.putIfAbsent(name, true); - player.kickPlayer("You are not the Owner of this account, please try another name!"); + player.kickPlayer("You are not the owner of this account. Please try another name!"); if (Settings.banUnsafeIp) plugin.getServer().banIP(ip); } @@ -225,7 +225,7 @@ public class AsynchronousJoin { ? m.retrieve(MessageKey.REGISTER_EMAIL_MESSAGE) : m.retrieve(MessageKey.REGISTER_MESSAGE); } - if (LimboCache.getInstance().getLimboPlayer(name) != null) { + if (msgInterval > 0 && LimboCache.getInstance().getLimboPlayer(name) != null) { BukkitTask msgTask = sched.runTaskAsynchronously(plugin, new MessageTask(plugin, name, msg, msgInterval)); LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgTask); } @@ -282,4 +282,30 @@ public class AsynchronousJoin { }); } + /** + * Return whether the name is restricted based on the restriction setting. + * + * @param name The name to check + * @param ip The IP address of the player + * @param domain The hostname of the IP address + * @return True if the name is restricted (IP/domain is not allowed for the given name), + * false if the restrictions are met or if the name has no restrictions to it + */ + private static boolean isNameRestricted(String name, String ip, String domain) { + boolean nameFound = false; + for (String entry : Settings.getRestrictedIp) { + String[] args = entry.split(";"); + String testName = args[0]; + String testIp = args[1]; + if (testName.equalsIgnoreCase(name)) { + nameFound = true; + if ((ip != null && testIp.equals(ip)) + || (domain != null && testIp.equalsIgnoreCase(domain))) { + return false; + } + } + } + return nameFound; + } + } diff --git a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java index 7109840f4..f2c3660e1 100644 --- a/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/AsynchronousLogin.java @@ -11,8 +11,11 @@ import fr.xephi.authme.permission.PlayerPermission; import fr.xephi.authme.security.RandomString; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; +import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.RegistrationSettings; import fr.xephi.authme.task.MessageTask; +import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.Utils; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -34,6 +37,7 @@ public class AsynchronousLogin { private final DataSource database; private final Messages m; private final String ip; + private final NewSetting settings; /** * Constructor for AsynchronousLogin. @@ -43,8 +47,10 @@ public class AsynchronousLogin { * @param forceLogin boolean * @param plugin AuthMe * @param data DataSource + * @param settings The settings */ - public AsynchronousLogin(Player player, String password, boolean forceLogin, AuthMe plugin, DataSource data) { + public AsynchronousLogin(Player player, String password, boolean forceLogin, AuthMe plugin, DataSource data, + NewSetting settings) { this.m = plugin.getMessages(); this.player = player; this.name = player.getName().toLowerCase(); @@ -54,6 +60,7 @@ public class AsynchronousLogin { this.plugin = plugin; this.database = data; this.ip = plugin.getIP(player); + this.settings = settings; } protected boolean needsCaptcha() { @@ -98,7 +105,7 @@ public class AsynchronousLogin { msg = m.retrieve(MessageKey.REGISTER_MESSAGE); } BukkitTask msgT = Bukkit.getScheduler().runTaskAsynchronously(plugin, - new MessageTask(plugin, name, msg, Settings.getWarnMessageInterval)); + new MessageTask(plugin, name, msg, settings.getProperty(RegistrationSettings.MESSAGE_INTERVAL))); LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgT); } return null; @@ -165,12 +172,10 @@ public class AsynchronousLogin { if (!forceLogin) m.send(player, MessageKey.LOGIN_SUCCESS); - displayOtherAccounts(auth, player); + displayOtherAccounts(auth); - if (Settings.recallEmail) { - if (email == null || email.isEmpty() || email.equalsIgnoreCase("your@email.com")) { - m.send(player, MessageKey.EMAIL_ADDED_SUCCESS); - } + if (Settings.recallEmail && (StringUtils.isEmpty(email) || "your@email.com".equalsIgnoreCase(email))) { + m.send(player, MessageKey.EMAIL_ADDED_SUCCESS); } if (!Settings.noConsoleSpam) { @@ -186,7 +191,7 @@ public class AsynchronousLogin { // task, we schedule it in the end // so that we can be sure, and have not to care if it might be // processed in other order. - ProcessSyncPlayerLogin syncPlayerLogin = new ProcessSyncPlayerLogin(player, plugin, database); + ProcessSyncPlayerLogin syncPlayerLogin = new ProcessSyncPlayerLogin(player, plugin, database, settings); if (syncPlayerLogin.getLimbo() != null) { if (syncPlayerLogin.getLimbo().getTimeoutTaskId() != null) { syncPlayerLogin.getLimbo().getTimeoutTaskId().cancel(); @@ -215,7 +220,7 @@ public class AsynchronousLogin { } } - public void displayOtherAccounts(PlayerAuth auth, Player p) { + public void displayOtherAccounts(PlayerAuth auth) { if (!Settings.displayOtherAccounts) { return; } @@ -223,30 +228,16 @@ public class AsynchronousLogin { return; } List auths = this.database.getAllAuthsByName(auth); - if (auths.isEmpty()) { + if (auths.isEmpty() || auths.size() == 1) { return; } - if (auths.size() == 1) { - return; - } - StringBuilder message = new StringBuilder("[AuthMe] "); - int i = 0; - for (String account : auths) { - i++; - message.append(account); - if (i != auths.size()) { - message.append(", "); - } else { - message.append('.'); - } - } - + String message = "[AuthMe] " + StringUtils.join(", ", auths) + "."; for (Player player : Utils.getOnlinePlayers()) { if (plugin.getPermissionsManager().hasPermission(player, PlayerPermission.SEE_OTHER_ACCOUNTS) || (player.getName().equals(this.player.getName()) && plugin.getPermissionsManager().hasPermission(player, PlayerPermission.SEE_OWN_ACCOUNTS))) { player.sendMessage("[AuthMe] The player " + auth.getNickname() + " has " + auths.size() + " accounts"); - player.sendMessage(message.toString()); + player.sendMessage(message); } } } diff --git a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java index 27c09174e..07c5b6d40 100644 --- a/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java +++ b/src/main/java/fr/xephi/authme/process/login/ProcessSyncPlayerLogin.java @@ -1,5 +1,7 @@ package fr.xephi.authme.process.login; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.HooksSettings; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -24,6 +26,8 @@ import fr.xephi.authme.settings.Settings; import fr.xephi.authme.util.Utils; import fr.xephi.authme.util.Utils.GroupType; +import static fr.xephi.authme.settings.properties.RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN; + /** */ public class ProcessSyncPlayerLogin implements Runnable { @@ -36,24 +40,26 @@ public class ProcessSyncPlayerLogin implements Runnable { private final DataSource database; private final PluginManager pm; private final JsonCache playerCache; + private final NewSetting settings; /** * Constructor for ProcessSyncPlayerLogin. * * @param player Player * @param plugin AuthMe - * @param data DataSource + * @param database DataSource */ public ProcessSyncPlayerLogin(Player player, AuthMe plugin, - DataSource data) { + DataSource database, NewSetting settings) { this.plugin = plugin; - this.database = data; + this.database = database; this.pm = plugin.getServer().getPluginManager(); this.player = player; this.name = player.getName().toLowerCase(); this.limbo = LimboCache.getInstance().getLimboPlayer(name); this.auth = database.getAuth(name); this.playerCache = new JsonCache(); + this.settings = settings; } /** @@ -152,7 +158,7 @@ public class ProcessSyncPlayerLogin implements Runnable { } } - if (Settings.protectInventoryBeforeLogInEnabled) { + if (settings.getProperty(PROTECT_INVENTORY_BEFORE_LOGIN)) { restoreInventory(); } @@ -188,27 +194,27 @@ public class ProcessSyncPlayerLogin implements Runnable { // Login is finish, display welcome message if we use email registration if (Settings.useWelcomeMessage && Settings.emailRegistration) if (Settings.broadcastWelcomeMessage) { - for (String s : Settings.welcomeMsg) { + for (String s : settings.getWelcomeMessage()) { Bukkit.getServer().broadcastMessage(plugin.replaceAllInfo(s, player)); } } else { - for (String s : Settings.welcomeMsg) { + for (String s : settings.getWelcomeMessage()) { player.sendMessage(plugin.replaceAllInfo(s, player)); } } - // Login is now finish , we can force all commands + // Login is now finished; we can force all commands forceCommands(); sendTo(); } private void sendTo() { - if (Settings.sendPlayerTo.isEmpty()) - return; - ByteArrayDataOutput out = ByteStreams.newDataOutput(); - out.writeUTF("Connect"); - out.writeUTF(Settings.sendPlayerTo); - player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray()); + if (!settings.getProperty(HooksSettings.BUNGEECORD_SERVER).isEmpty()) { + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeUTF("Connect"); + out.writeUTF(settings.getProperty(HooksSettings.BUNGEECORD_SERVER)); + player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray()); + } } } diff --git a/src/main/java/fr/xephi/authme/process/logout/ProcessSyncronousPlayerLogout.java b/src/main/java/fr/xephi/authme/process/logout/ProcessSyncronousPlayerLogout.java index 236c8cd49..5a8133b79 100644 --- a/src/main/java/fr/xephi/authme/process/logout/ProcessSyncronousPlayerLogout.java +++ b/src/main/java/fr/xephi/authme/process/logout/ProcessSyncronousPlayerLogout.java @@ -49,6 +49,13 @@ public class ProcessSyncronousPlayerLogout implements Runnable { player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray()); } + protected void restoreSpeedEffect() { + if (Settings.isRemoveSpeedEnabled) { + player.setWalkSpeed(0.0F); + player.setFlySpeed(0.0F); + } + } + /** * Method run. * @@ -77,6 +84,7 @@ public class ProcessSyncronousPlayerLogout implements Runnable { if (Settings.applyBlindEffect) player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, Settings.getRegistrationTimeout * 20, 2)); player.setOp(false); + restoreSpeedEffect(); // Player is now logout... Time to fire event ! Bukkit.getServer().getPluginManager().callEvent(new LogoutEvent(player)); if (Settings.bungee) diff --git a/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java b/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java index 80dd49b99..00b477440 100644 --- a/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java +++ b/src/main/java/fr/xephi/authme/process/quit/AsynchronousQuit.java @@ -75,18 +75,21 @@ public class AsynchronousQuit { } if (Settings.isSessionsEnabled && !isKick) { if (Settings.getSessionTimeout != 0) { - BukkitTask task = plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, new Runnable() { + if (plugin.isEnabled()) { + BukkitTask task = plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, new Runnable() { - @Override - public void run() { - PlayerCache.getInstance().removePlayer(name); - if (database.isLogged(name)) - database.setUnlogged(name); - plugin.sessions.remove(name); - } + @Override + public void run() { + postLogout(); + } - }, Settings.getSessionTimeout * 20 * 60); - plugin.sessions.put(name, task); + }, Settings.getSessionTimeout * 20 * 60); + + plugin.sessions.put(name, task); + } else { + //plugin is disable we canno schedule more tasks so run it directly here + postLogout(); + } } } else { PlayerCache.getInstance().removePlayer(name); @@ -94,6 +97,15 @@ public class AsynchronousQuit { } plugin.realIp.remove(name); - Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new ProcessSyncronousPlayerQuit(plugin, player, isOp, needToChange)); + if (plugin.isEnabled()) { + Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new ProcessSyncronousPlayerQuit(plugin, player, isOp, needToChange)); + } + } + + private void postLogout() { + PlayerCache.getInstance().removePlayer(name); + if (database.isLogged(name)) + database.setUnlogged(name); + plugin.sessions.remove(name); } } diff --git a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java b/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java index 38c001856..16c469e5c 100644 --- a/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/AsyncRegister.java @@ -9,6 +9,7 @@ import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; import fr.xephi.authme.permission.PlayerPermission; import fr.xephi.authme.security.crypts.HashedPassword; +import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; import org.bukkit.entity.Player; @@ -24,8 +25,10 @@ public class AsyncRegister { private final AuthMe plugin; private final DataSource database; private final Messages m; + private final NewSetting settings; - public AsyncRegister(Player player, String password, String email, AuthMe plugin, DataSource data) { + public AsyncRegister(Player player, String password, String email, AuthMe plugin, DataSource data, + NewSetting settings) { this.m = plugin.getMessages(); this.player = player; this.password = password; @@ -34,6 +37,7 @@ public class AsyncRegister { this.plugin = plugin; this.database = data; this.ip = plugin.getIP(player); + this.settings = settings; } private boolean preRegisterCheck() throws Exception { @@ -75,14 +79,13 @@ public class AsyncRegister { if (!preRegisterCheck()) { return; } - if (!email.isEmpty() && !email.equals("")) { + if (email != null && !email.isEmpty()) { emailRegister(); } else { passwordRegister(); } } catch (Exception e) { - ConsoleLogger.showError(e.getMessage()); - ConsoleLogger.writeStackTrace(e); + ConsoleLogger.logException("Error during async register process", e); m.send(player, MessageKey.ERROR); } } @@ -137,7 +140,7 @@ public class AsyncRegister { plugin.getManagement().performLogin(player, "dontneed", true); } plugin.otherAccounts.addPlayer(player.getUniqueId()); - ProcessSyncPasswordRegister sync = new ProcessSyncPasswordRegister(player, plugin); + ProcessSyncPasswordRegister sync = new ProcessSyncPasswordRegister(player, plugin, settings); plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, sync); } } diff --git a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java index 1b4b0555e..6d875ee0f 100644 --- a/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java +++ b/src/main/java/fr/xephi/authme/process/register/ProcessSyncPasswordRegister.java @@ -1,5 +1,7 @@ package fr.xephi.authme.process.register; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.HooksSettings; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.potion.PotionEffectType; @@ -30,6 +32,7 @@ public class ProcessSyncPasswordRegister implements Runnable { protected final String name; private final AuthMe plugin; private final Messages m; + private final NewSetting settings; /** * Constructor for ProcessSyncPasswordRegister. @@ -37,11 +40,12 @@ public class ProcessSyncPasswordRegister implements Runnable { * @param player Player * @param plugin AuthMe */ - public ProcessSyncPasswordRegister(Player player, AuthMe plugin) { + public ProcessSyncPasswordRegister(Player player, AuthMe plugin, NewSetting settings) { this.m = plugin.getMessages(); this.player = player; this.name = player.getName().toLowerCase(); this.plugin = plugin; + this.settings = settings; } private void sendBungeeMessage() { @@ -63,11 +67,6 @@ public class ProcessSyncPasswordRegister implements Runnable { } } - /** - * Method forceLogin. - * - * @param player Player - */ private void forceLogin(Player player) { Utils.teleportToSpawn(player); LimboCache cache = LimboCache.getInstance(); @@ -88,11 +87,6 @@ public class ProcessSyncPasswordRegister implements Runnable { } } - /** - * Method run. - * - * @see java.lang.Runnable#run() - */ @Override public void run() { LimboPlayer limbo = LimboCache.getInstance().getLimboPlayer(name); @@ -141,11 +135,11 @@ public class ProcessSyncPasswordRegister implements Runnable { // Register is finish and player is logged, display welcome message if (Settings.useWelcomeMessage) { if (Settings.broadcastWelcomeMessage) { - for (String s : Settings.welcomeMsg) { + for (String s : settings.getWelcomeMessage()) { plugin.getServer().broadcastMessage(plugin.replaceAllInfo(s, player)); } } else { - for (String s : Settings.welcomeMsg) { + for (String s : settings.getWelcomeMessage()) { player.sendMessage(plugin.replaceAllInfo(s, player)); } } @@ -161,18 +155,18 @@ public class ProcessSyncPasswordRegister implements Runnable { sendBungeeMessage(); } - // Register is now finish , we can force all commands + // Register is now finished; we can force all commands forceCommands(); sendTo(); } private void sendTo() { - if (Settings.sendPlayerTo.isEmpty()) - return; - ByteArrayDataOutput out = ByteStreams.newDataOutput(); - out.writeUTF("Connect"); - out.writeUTF(Settings.sendPlayerTo); - player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray()); + if (!settings.getProperty(HooksSettings.BUNGEECORD_SERVER).isEmpty()) { + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeUTF("Connect"); + out.writeUTF(settings.getProperty(HooksSettings.BUNGEECORD_SERVER)); + player.sendPluginMessage(plugin, "BungeeCord", out.toByteArray()); + } } } diff --git a/src/main/java/fr/xephi/authme/security/crypts/CRAZYCRYPT1.java b/src/main/java/fr/xephi/authme/security/crypts/CRAZYCRYPT1.java index cd8af9249..828a38d8c 100644 --- a/src/main/java/fr/xephi/authme/security/crypts/CRAZYCRYPT1.java +++ b/src/main/java/fr/xephi/authme/security/crypts/CRAZYCRYPT1.java @@ -2,11 +2,6 @@ package fr.xephi.authme.security.crypts; import fr.xephi.authme.security.HashUtils; import fr.xephi.authme.security.MessageDigestAlgorithm; -import fr.xephi.authme.security.crypts.description.Usage; -import fr.xephi.authme.security.crypts.description.Recommendation; -import fr.xephi.authme.security.crypts.description.SaltType; -import fr.xephi.authme.security.crypts.description.HasSalt; - import java.nio.charset.Charset; import java.security.MessageDigest; diff --git a/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java b/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java index cb1719cb1..ee1645257 100644 --- a/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java +++ b/src/main/java/fr/xephi/authme/settings/CustomConfiguration.java @@ -77,8 +77,7 @@ public abstract class CustomConfiguration extends YamlConfiguration { return true; } } catch (Exception e) { - ConsoleLogger.writeStackTrace(e); - ConsoleLogger.showError("Failed to load config from JAR"); + ConsoleLogger.logException("Failed to load config from JAR", e); } } return false; diff --git a/src/main/java/fr/xephi/authme/settings/NewSetting.java b/src/main/java/fr/xephi/authme/settings/NewSetting.java new file mode 100644 index 000000000..29e833d0a --- /dev/null +++ b/src/main/java/fr/xephi/authme/settings/NewSetting.java @@ -0,0 +1,274 @@ +package fr.xephi.authme.settings; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.io.Files; +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.settings.domain.Property; +import fr.xephi.authme.settings.properties.PluginSettings; +import fr.xephi.authme.settings.properties.RegistrationSettings; +import fr.xephi.authme.settings.properties.SettingsFieldRetriever; +import fr.xephi.authme.settings.propertymap.PropertyMap; +import fr.xephi.authme.util.CollectionUtils; +import fr.xephi.authme.util.StringUtils; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static fr.xephi.authme.settings.SettingsMigrationService.copyFileFromResource; + +/** + * The new settings manager. + */ +public class NewSetting { + + private final File pluginFolder; + private final File configFile; + private FileConfiguration configuration; + /** The file with the localized messages based on {@link PluginSettings#MESSAGES_LANGUAGE}. */ + private File messagesFile; + private List welcomeMessage; + private String emailMessage; + + /** + * Constructor. Checks the given {@link FileConfiguration} object for completeness. + * + * @param configFile The configuration file + * @param pluginFolder The AuthMe plugin folder + */ + public NewSetting(File configFile, File pluginFolder) { + this.configuration = YamlConfiguration.loadConfiguration(configFile); + this.configFile = configFile; + this.pluginFolder = pluginFolder; + validateAndLoadOptions(); + } + + /** + * Constructor for testing purposes, allowing more options. + * + * @param configuration The FileConfiguration object to use + * @param configFile The file to write to + * @param propertyMap The property map whose properties should be verified for presence, or null to skip this + */ + @VisibleForTesting + NewSetting(FileConfiguration configuration, File configFile, PropertyMap propertyMap) { + this.configuration = configuration; + this.configFile = configFile; + this.pluginFolder = new File(""); + + if (propertyMap != null && SettingsMigrationService.checkAndMigrate(configuration, propertyMap, pluginFolder)) { + save(propertyMap); + } + } + + /** + * Get the given property from the configuration. + * + * @param property The property to retrieve + * @param The property's type + * @return The property's value + */ + public T getProperty(Property property) { + return property.getFromFile(configuration); + } + + /** + * Set a new value for the given property. + * + * @param property The property to modify + * @param value The new value to assign to the property + * @param The property's type + */ + public void setProperty(Property property, T value) { + configuration.set(property.getPath(), value); + } + + /** + * Save the config file. Use after migrating one or more settings. + */ + public void save() { + save(SettingsFieldRetriever.getAllPropertyFields()); + } + + /** + * Return the messages file based on the messages language config. + * + * @return The messages file to read messages from + */ + public File getMessagesFile() { + return messagesFile; + } + + public String getEmailMessage() { + return emailMessage; + } + + public List getWelcomeMessage() { + return welcomeMessage; + } + + /** + * Reload the configuration. + */ + public void reload() { + configuration = YamlConfiguration.loadConfiguration(configFile); + validateAndLoadOptions(); + } + + private void save(PropertyMap propertyMap) { + try (FileWriter writer = new FileWriter(configFile)) { + Yaml simpleYaml = newYaml(false); + Yaml singleQuoteYaml = newYaml(true); + + writer.write(""); + // Contains all but the last node of the setting, e.g. [DataSource, mysql] for "DataSource.mysql.username" + List currentPath = new ArrayList<>(); + for (Map.Entry, String[]> entry : propertyMap.entrySet()) { + Property property = entry.getKey(); + + // Handle properties + List propertyPath = Arrays.asList(property.getPath().split("\\.")); + List commonPathParts = CollectionUtils.filterCommonStart( + currentPath, propertyPath.subList(0, propertyPath.size() - 1)); + List newPathParts = CollectionUtils.getRange(propertyPath, commonPathParts.size()); + + if (commonPathParts.isEmpty()) { + writer.append("\n"); + } + + int indentationLevel = commonPathParts.size(); + if (newPathParts.size() > 1) { + for (String path : newPathParts.subList(0, newPathParts.size() - 1)) { + writer.append("\n") + .append(indent(indentationLevel)) + .append(path) + .append(": "); + ++indentationLevel; + } + } + for (String comment : entry.getValue()) { + writer.append("\n") + .append(indent(indentationLevel)) + .append("# ") + .append(comment); + } + writer.append("\n") + .append(indent(indentationLevel)) + .append(CollectionUtils.getRange(newPathParts, newPathParts.size() - 1).get(0)) + .append(": ") + .append(toYaml(property, indentationLevel, simpleYaml, singleQuoteYaml)); + + currentPath = propertyPath.subList(0, propertyPath.size() - 1); + } + writer.flush(); + writer.close(); + } catch (IOException e) { + ConsoleLogger.logException("Could not save config file:", e); + } + } + + 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")); + } + + private File buildMessagesFile() { + String languageCode = getProperty(PluginSettings.MESSAGES_LANGUAGE); + File messagesFile = buildMessagesFileFromCode(languageCode); + if (messagesFile.exists()) { + return messagesFile; + } + + return copyFileFromResource(messagesFile, buildMessagesFilePathFromCode(languageCode)) + ? messagesFile + : buildMessagesFileFromCode("en"); + } + + private File buildMessagesFileFromCode(String language) { + 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 (copyFileFromResource(welcomeFile, "welcome.txt")) { + try { + return Files.readLines(welcomeFile, charset); + } catch (IOException e) { + ConsoleLogger.logException("Failed to read file '" + welcomeFile.getPath() + "':", e); + } + } + } + return new ArrayList<>(0); + } + + private String readEmailMessage() { + final File emailFile = new File(pluginFolder, "email.html"); + final Charset charset = Charset.forName("UTF-8"); + if (copyFileFromResource(emailFile, "email.html")) { + try { + return StringUtils.join("", Files.readLines(emailFile, charset)); + } catch (IOException e) { + ConsoleLogger.logException("Failed to read file '" + emailFile.getPath() + "':", e); + } + } + return ""; + } + + private static Yaml newYaml(boolean useSingleQuotes) { + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + options.setAllowUnicode(true); + if (useSingleQuotes) { + options.setDefaultScalarStyle(DumperOptions.ScalarStyle.SINGLE_QUOTED); + } + return new Yaml(options); + } + + private static String join(String delimiter, String[] items) { + StringBuilder sb = new StringBuilder(); + String delim = ""; + for (String item : items) { + sb.append(delim).append(item); + delim = delimiter; + } + return sb.toString(); + } + + private static String indent(int level) { + // We use an indentation of 4 spaces + StringBuilder sb = new StringBuilder(level * 4); + for (int i = 0; i < level; ++i) { + sb.append(" "); + } + return sb.toString(); + } + +} diff --git a/src/main/java/fr/xephi/authme/settings/Settings.java b/src/main/java/fr/xephi/authme/settings/Settings.java index 9f4285b64..ff7116213 100644 --- a/src/main/java/fr/xephi/authme/settings/Settings.java +++ b/src/main/java/fr/xephi/authme/settings/Settings.java @@ -1,42 +1,32 @@ package fr.xephi.authme.settings; -import com.google.common.base.Charsets; -import com.google.common.io.Files; import fr.xephi.authme.AuthMe; import fr.xephi.authme.ConsoleLogger; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.datasource.DataSource.DataSourceType; import fr.xephi.authme.security.HashAlgorithm; -import fr.xephi.authme.util.StringUtils; import fr.xephi.authme.util.Wrapper; -import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.configuration.file.FileConfiguration; -import java.io.BufferedReader; -import java.io.BufferedWriter; import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Iterator; import java.util.List; import java.util.regex.Pattern; /** + * Old settings manager. See {@link NewSetting} for the new manager. */ public final class Settings { public static final File PLUGIN_FOLDER = Wrapper.getInstance().getDataFolder(); public static final File MODULE_FOLDER = new File(PLUGIN_FOLDER, "modules"); public static final File CACHE_FOLDER = new File(PLUGIN_FOLDER, "cache"); - public static final File AUTH_FILE = new File(PLUGIN_FOLDER, "auths.db"); - public static final File EMAIL_FILE = new File(PLUGIN_FOLDER, "email.html"); - public static final File SETTINGS_FILE = new File(PLUGIN_FOLDER, "config.yml"); + private static final File SETTINGS_FILE = new File(PLUGIN_FOLDER, "config.yml"); public static final File LOG_FILE = new File(PLUGIN_FOLDER, "authme.log"); // This is not an option! public static boolean antiBotInAction = false; - public static File messageFile; public static List allowCommands; public static List getJoinPermissions; public static List getUnrestrictedName; @@ -49,7 +39,6 @@ public final class Settings { public static List forceCommandsAsConsole; public static List forceRegisterCommands; public static List forceRegisterCommandsAsConsole; - public static List welcomeMsg; public static List unsafePasswords; public static List emailBlacklist; public static List emailWhitelist; @@ -66,8 +55,7 @@ public final class Settings { isSaveQuitLocationEnabled, isForceSurvivalModeEnabled, isCachingEnabled, isKickOnWrongPasswordEnabled, enablePasswordConfirmation, - protectInventoryBeforeLogInEnabled, isBackupActivated, - isBackupOnStart, isBackupOnStop, isStopEnabled, reloadSupport, + protectInventoryBeforeLogInEnabled, isStopEnabled, reloadSupport, rakamakUseIp, noConsoleSpam, removePassword, displayOtherAccounts, useCaptcha, emailRegistration, multiverse, bungee, banUnsafeIp, doubleEmailCheck, sessionExpireOnIpChange, @@ -77,21 +65,20 @@ public final class Settings { enableProtection, enableAntiBot, recallEmail, useWelcomeMessage, broadcastWelcomeMessage, forceRegKick, forceRegLogin, checkVeryGames, delayJoinLeaveMessages, noTeleport, applyBlindEffect, - kickPlayersBeforeStopping, + kickPlayersBeforeStopping, allowAllCommandsIfRegIsOptional, customAttributes, generateImage, isRemoveSpeedEnabled, preventOtherCase; - public static String helpHeader, getNickRegex, getUnloggedinGroup, getMySQLHost, + public static String getNickRegex, getUnloggedinGroup, getMySQLHost, getMySQLPort, getMySQLUsername, getMySQLPassword, getMySQLDatabase, getMySQLTablename, getMySQLColumnName, getMySQLColumnPassword, getMySQLColumnIp, getMySQLColumnLastLogin, getMySQLColumnSalt, getMySQLColumnGroup, getMySQLColumnEmail, unRegisteredGroup, backupWindowsPath, getRegisteredGroup, - messagesLanguage, getMySQLlastlocX, getMySQLlastlocY, + getMySQLlastlocX, getMySQLlastlocY, getMySQLlastlocZ, rakamakUsers, rakamakUsersIp, getmailAccount, - getmailPassword, getmailSMTP, getMySQLColumnId, getmailSenderName, - getMailSubject, getMailText, getMySQLlastlocWorld, defaultWorld, + getMySQLColumnId, getMySQLlastlocWorld, defaultWorld, getPhpbbPrefix, getWordPressPrefix, getMySQLColumnLogged, spawnPriority, crazyloginFileName, getPassRegex, - getMySQLColumnRealName, emailOauth2Token, sendPlayerTo; + getMySQLColumnRealName, sendPlayerTo; public static int getWarnMessageInterval, getSessionTimeout, getRegistrationTimeout, getMaxNickLength, getMinNickLength, getPasswordMinLen, getMovementRadius, getmaxRegPerIp, @@ -100,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; @@ -112,34 +99,11 @@ 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); - if (exist) { - instance.mergeConfig(); - } + configFile = plugin.getConfig(); loadVariables(); - if (exist) { - instance.saveDefaults(); - } - messageFile = new File(PLUGIN_FOLDER, "messages" + File.separator + "messages_" + messagesLanguage + ".yml"); } public static void loadVariables() { - helpHeader = configFile.getString("settings.helpHeader", "AuthMeReloaded"); - messagesLanguage = checkLang(configFile.getString("settings.messagesLanguage", "en").toLowerCase()); isPermissionCheckEnabled = configFile.getBoolean("permission.EnablePermissionCheck", false); isForcedRegistrationEnabled = configFile.getBoolean("settings.registration.force", true); isRegistrationEnabled = configFile.getBoolean("settings.registration.enabled", true); @@ -204,13 +168,11 @@ public final class Settings { plugin.checkProtocolLib(); passwordMaxLength = configFile.getInt("settings.security.passwordMaxLength", 20); - isBackupActivated = configFile.getBoolean("BackupSystem.ActivateBackup", false); - isBackupOnStart = configFile.getBoolean("BackupSystem.OnServerStart", false); - isBackupOnStop = configFile.getBoolean("BackupSystem.OnServeStop", false); backupWindowsPath = configFile.getString("BackupSystem.MysqlWindowsPath", "C:\\Program Files\\MySQL\\MySQL Server 5.1\\"); isStopEnabled = configFile.getBoolean("Security.SQLProblem.stopServer", true); reloadSupport = configFile.getBoolean("Security.ReloadCommand.useReloadCommandSupport", true); + allowAllCommandsIfRegIsOptional = configFile.getBoolean("settings.restrictions.allowAllCommandsIfRegistrationIsOptional", false); allowCommands = new ArrayList<>(); allowCommands.addAll(Arrays.asList("/login", "/l", "/register", "/reg", "/email", "/captcha")); for (String cmd : configFile.getStringList("settings.restrictions.allowCommands")) { @@ -226,19 +188,14 @@ public final class Settings { noConsoleSpam = configFile.getBoolean("Security.console.noConsoleSpam", false); removePassword = configFile.getBoolean("Security.console.removePassword", true); getmailAccount = configFile.getString("Email.mailAccount", ""); - getmailPassword = configFile.getString("Email.mailPassword", ""); - getmailSMTP = configFile.getString("Email.mailSMTP", "smtp.gmail.com"); getMailPort = configFile.getInt("Email.mailPort", 465); getRecoveryPassLength = configFile.getInt("Email.RecoveryPasswordLength", 8); getMySQLOtherUsernameColumn = configFile.getStringList("ExternalBoardOptions.mySQLOtherUsernameColumns"); displayOtherAccounts = configFile.getBoolean("settings.restrictions.displayOtherAccounts", true); getMySQLColumnId = configFile.getString("DataSource.mySQLColumnId", "id"); - getmailSenderName = configFile.getString("Email.mailSenderName", ""); useCaptcha = configFile.getBoolean("Security.captcha.useCaptcha", false); maxLoginTry = configFile.getInt("Security.captcha.maxLoginTry", 5); captchaLength = configFile.getInt("Security.captcha.captchaLength", 5); - getMailSubject = configFile.getString("Email.mailSubject", "Your new AuthMe Password"); - getMailText = loadEmailText(); emailRegistration = configFile.getBoolean("settings.registration.enableEmailRegistrationSystem", false); saltLength = configFile.getInt("settings.security.doubleMD5SaltLength", 8); getmaxRegPerEmail = configFile.getInt("Email.maxRegPerEmail", 1); @@ -297,34 +254,8 @@ public final class Settings { generateImage = configFile.getBoolean("Email.generateImage", false); preventOtherCase = configFile.getBoolean("settings.preventOtherCase", false); kickPlayersBeforeStopping = configFile.getBoolean("Security.stop.kickPlayersBeforeStopping", true); - emailOauth2Token = configFile.getString("Email.emailOauth2Token", ""); sendPlayerTo = configFile.getString("Hooks.sendPlayerTo", ""); - // Load the welcome message - getWelcomeMessage(); - - } - - private static String loadEmailText() { - if (!EMAIL_FILE.exists()) { - plugin.saveResource("email.html", false); - } - try { - return Files.toString(EMAIL_FILE, Charsets.UTF_8); - } catch (IOException e) { - ConsoleLogger.showError("Error loading email text: " + StringUtils.formatException(e)); - ConsoleLogger.writeStackTrace(e); - return ""; - } - } - - /** - * @param key the key to set - * @param value the value to set - */ - public static void setValue(String key, Object value) { - instance.set(key, value); - save(); } /** @@ -357,49 +288,12 @@ public final class Settings { } } - /** - * Config option for setting and check restricted user by username;ip , - * return false if ip and name doesn't match with player that join the - * server, so player has a restricted access - * - * @param name String - * @param ip String - * @param domain String - * - * @return boolean - */ - public static boolean getRestrictedIp(String name, String ip, String domain) { - - Iterator iterator = getRestrictedIp.iterator(); - boolean trueOnce = false; - boolean nameFound = false; - while (iterator.hasNext()) { - String[] args = iterator.next().split(";"); - String testName = args[0]; - String testIp = args[1]; - if (testName.equalsIgnoreCase(name)) { - nameFound = true; - if (ip != null) { - if (testIp.equalsIgnoreCase(ip)) { - trueOnce = true; - } - } - if (domain != null) { - if (testIp.equalsIgnoreCase(domain)) { - trueOnce = true; - } - } - } - } - return !nameFound || trueOnce; - } - /** * Saves the configuration to disk * * @return True if saved successfully */ - public static boolean save() { + private static boolean save() { try { configFile.save(SETTINGS_FILE); return true; @@ -415,7 +309,7 @@ public final class Settings { * * @return String */ - public static String checkLang(String lang) { + private static String checkLang(String lang) { if (new File(PLUGIN_FOLDER, "messages" + File.separator + "messages_" + lang + ".yml").exists()) { ConsoleLogger.info("Set Language to: " + lang); return lang; @@ -443,321 +337,6 @@ public final class Settings { } } - private static void getWelcomeMessage() { - AuthMe plugin = AuthMe.getInstance(); - welcomeMsg = new ArrayList<>(); - if (!useWelcomeMessage) { - return; - } - if (!(new File(plugin.getDataFolder() + File.separator + "welcome.txt").exists())) { - try { - FileWriter fw = new FileWriter(plugin.getDataFolder() + File.separator + "welcome.txt", true); - BufferedWriter w = new BufferedWriter(fw); - w.write("Welcome {PLAYER} on {SERVER} server"); - w.newLine(); - w.write("This server uses " + AuthMe.getPluginName() + " protection!"); - w.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - try { - FileReader fr = new FileReader(plugin.getDataFolder() + File.separator + "welcome.txt"); - BufferedReader br = new BufferedReader(fr); - String line; - while ((line = br.readLine()) != null) { - welcomeMsg.add(line); - } - br.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - /** - * Method isEmailCorrect. - * - * @param email String - * - * @return boolean - */ - public static boolean isEmailCorrect(String email) { - if (!email.contains("@")) - return false; - if (email.equalsIgnoreCase("your@email.com")) - return false; - String emailDomain = email.split("@")[1]; - boolean correct = true; - if (emailWhitelist != null && !emailWhitelist.isEmpty()) { - for (String domain : emailWhitelist) { - if (!domain.equalsIgnoreCase(emailDomain)) { - correct = false; - } else { - correct = true; - break; - } - } - return correct; - } - if (emailBlacklist != null && !emailBlacklist.isEmpty()) { - for (String domain : emailBlacklist) { - if (domain.equalsIgnoreCase(emailDomain)) { - correct = false; - break; - } - } - } - return correct; - } - - public void mergeConfig() { - boolean changes = false; - if (contains("Xenoforo.predefinedSalt")) { - set("Xenoforo.predefinedSalt", null); - changes = true; - } - if (!contains("Protection.enableProtection")) { - set("Protection.enableProtection", false); - changes = true; - } - if (!contains("settings.restrictions.removeSpeed")) { - set("settings.restrictions.removeSpeed", true); - changes = true; - } - if (!contains("Protection.countries")) { - countries = new ArrayList<>(); - countries.add("US"); - countries.add("GB"); - set("Protection.countries", countries); - changes = true; - } - if (!contains("Protection.enableAntiBot")) { - set("Protection.enableAntiBot", false); - changes = true; - } - if (!contains("Protection.antiBotSensibility")) { - set("Protection.antiBotSensibility", 5); - changes = true; - } - if (!contains("Protection.antiBotDuration")) { - set("Protection.antiBotDuration", 10); - changes = true; - } - if (!contains("settings.forceCommands")) { - set("settings.forceCommands", new ArrayList()); - changes = true; - } - if (!contains("settings.forceCommandsAsConsole")) { - set("settings.forceCommandsAsConsole", new ArrayList()); - changes = true; - } - if (!contains("Email.recallPlayers")) { - set("Email.recallPlayers", false); - changes = true; - } - if (!contains("Email.delayRecall")) { - set("Email.delayRecall", 5); - changes = true; - } - if (!contains("settings.useWelcomeMessage")) { - set("settings.useWelcomeMessage", true); - changes = true; - } - if (!contains("settings.restrictions.enablePasswordConfirmation")) { - set("settings.restrictions.enablePasswordConfirmation", true); - changes = true; - } - if (contains("settings.restrictions.enablePasswordVerifier")) { - set("settings.restrictions.enablePasswordVerifier", null); - changes = true; - } - if (!contains("settings.security.unsafePasswords")) { - List str = new ArrayList<>(); - str.add("123456"); - str.add("password"); - set("settings.security.unsafePasswords", str); - changes = true; - } - if (!contains("Protection.countriesBlacklist")) { - countriesBlacklist = new ArrayList<>(); - countriesBlacklist.add("A1"); - set("Protection.countriesBlacklist", countriesBlacklist); - changes = true; - } - if (!contains("settings.helpHeader")) { - set("settings.helpHeader", "AuthMeReloaded"); - changes = true; - } - if (!contains("settings.broadcastWelcomeMessage")) { - set("settings.broadcastWelcomeMessage", false); - changes = true; - } - if (!contains("settings.registration.forceKickAfterRegister")) { - set("settings.registration.forceKickAfterRegister", false); - changes = true; - } - if (!contains("settings.registration.forceLoginAfterRegister")) { - set("settings.registration.forceLoginAfterRegister", false); - changes = true; - } - if (!contains("DataSource.mySQLColumnLogged")) { - set("DataSource.mySQLColumnLogged", "isLogged"); - changes = true; - } - if (!contains("settings.restrictions.spawnPriority")) { - set("settings.restrictions.spawnPriority", "authme,essentials,multiverse,default"); - changes = true; - } - if (!contains("settings.restrictions.maxLoginPerIp")) { - set("settings.restrictions.maxLoginPerIp", 0); - changes = true; - } - if (!contains("settings.restrictions.maxJoinPerIp")) { - set("settings.restrictions.maxJoinPerIp", 0); - changes = true; - } - if (!contains("VeryGames.enableIpCheck")) { - set("VeryGames.enableIpCheck", false); - changes = true; - } - if (configFile.getString("settings.restrictions.allowedNicknameCharacters").equals("[a-zA-Z0-9_?]*")) { - set("settings.restrictions.allowedNicknameCharacters", "[a-zA-Z0-9_]*"); - changes = true; - } - if (contains("settings.delayJoinMessage")) { - set("settings.delayJoinMessage", null); - changes = true; - } - if (!contains("settings.delayJoinLeaveMessages")) { - set("settings.delayJoinLeaveMessages", true); - changes = true; - } - if (!contains("settings.restrictions.noTeleport")) { - set("settings.restrictions.noTeleport", false); - changes = true; - } - if (contains("Converter.Rakamak.newPasswordHash")) { - set("Converter.Rakamak.newPasswordHash", null); - changes = true; - } - if (!contains("Converter.CrazyLogin.fileName")) { - set("Converter.CrazyLogin.fileName", "accounts.db"); - changes = true; - } - if (!contains("settings.restrictions.allowedPasswordCharacters")) { - set("settings.restrictions.allowedPasswordCharacters", "[\\x21-\\x7E]*"); - changes = true; - } - if (!contains("settings.applyBlindEffect")) { - set("settings.applyBlindEffect", false); - changes = true; - } - if (!contains("Email.emailBlacklisted")) { - set("Email.emailBlacklisted", new ArrayList()); - changes = true; - } - if (contains("Performances")) { - set("Performances", null); - changes = true; - } - if (contains("Passpartu.enablePasspartu")) { - set("Passpartu.enablePasspartu", null); - changes = true; - } - if (contains("Passpartu")) { - set("Passpartu", null); - changes = true; - } - if (!contains("Email.emailWhitelisted")) { - set("Email.emailWhitelisted", new ArrayList()); - changes = true; - } - if (!contains("settings.forceRegisterCommands")) { - set("settings.forceRegisterCommands", new ArrayList()); - changes = true; - } - if (!contains("settings.forceRegisterCommandsAsConsole")) { - set("settings.forceRegisterCommandsAsConsole", new ArrayList()); - changes = true; - } - if (!contains("Hooks.customAttributes")) { - set("Hooks.customAttributes", false); - changes = true; - } - if (!contains("Purge.removePermissions")) { - set("Purge.removePermissions", false); - changes = true; - } - if (contains("Hooks.notifications")) { - set("Hooks.notifications", null); - changes = true; - } - if (contains("Hooks.chestshop")) { - set("Hooks.chestshop", null); - changes = true; - } - if (contains("Hooks.legacyChestshop")) { - set("Hooks.legacyChestshop", null); - changes = true; - } - if (!contains("Email.generateImage")) { - set("Email.generateImage", false); - changes = true; - } - if (!contains("DataSource.mySQLRealName")) { - set("DataSource.mySQLRealName", "realname"); - changes = true; - } - - if (!contains("settings.preventOtherCase")) { - set("settings.preventOtherCase", false); - changes = true; - } - - if (contains("Email.mailText")) { - set("Email.mailText", null); - ConsoleLogger.showError("Remove Email.mailText from config, we now use the email.html file"); - } - - if (!contains("Security.stop.kickPlayersBeforeStopping")) { - set("Security.stop.kickPlayersBeforeStopping", true); - changes = true; - } - - if (!contains("Email.emailOauth2Token")) - set("Email.emailOauth2Token", ""); - - if (!contains("Hooks.sendPlayerTo")) { - set("Hooks.sendPlayerTo", ""); - changes = true; - } - - if (changes) { - save(); - plugin.getLogger().warning("Merged new Config Options - I'm not an error, please don't report me"); - plugin.getLogger().warning("Please check your config.yml file for new configs!"); - } - } - - /** - * @param path - * - * @return - */ - private static boolean contains(String path) { - return configFile.contains(path); - } - - // public because it's used in AuthMe at one place - - /** - * @param path String - * @param value String - */ - public void set(String path, Object value) { - configFile.set(path, value); - } - /** * Saves current configuration (plus defaults) to disk. *

@@ -765,7 +344,7 @@ public final class Settings { * * @return True if saved successfully */ - public final boolean saveDefaults() { + private boolean saveDefaults() { configFile.options() .copyDefaults(true) .copyHeader(true); diff --git a/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java new file mode 100644 index 000000000..0a41301c7 --- /dev/null +++ b/src/main/java/fr/xephi/authme/settings/SettingsMigrationService.java @@ -0,0 +1,139 @@ +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 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. + */ +public final class SettingsMigrationService { + + private SettingsMigrationService() { + } + + /** + * Checks the config file and does any necessary migrations. + * + * @param configuration The file configuration to check and migrate + * @param propertyMap The property map of all existing properties + * @param pluginFolder The plugin folder + * @return True if there is a change and the config must be saved, false if the config is up-to-date + */ + public static boolean checkAndMigrate(FileConfiguration configuration, PropertyMap propertyMap, File pluginFolder) { + return performMigrations(configuration, pluginFolder) || hasDeprecatedProperties(configuration) + || !containsAllSettings(configuration, propertyMap); + } + + private static boolean performMigrations(FileConfiguration configuration, File pluginFolder) { + boolean changes = false; + if ("[a-zA-Z0-9_?]*".equals(configuration.getString(ALLOWED_NICKNAME_CHARACTERS.getPath()))) { + configuration.set(ALLOWED_NICKNAME_CHARACTERS.getPath(), "[a-zA-Z0-9_]*"); + changes = true; + } + changes = changes || performMailTextToFileMigration(configuration, pluginFolder); + + return changes; + } + + @VisibleForTesting + static boolean containsAllSettings(FileConfiguration configuration, PropertyMap propertyMap) { + for (Property property : propertyMap.keySet()) { + if (!property.isPresent(configuration)) { + return false; + } + } + return true; + } + + private static boolean hasDeprecatedProperties(FileConfiguration configuration) { + String[] deprecatedProperties = { + "Converter.Rakamak.newPasswordHash", "Hooks.chestshop", "Hooks.legacyChestshop", "Hooks.notifications", + "Passpartu", "Performances", "settings.delayJoinMessage", "settings.restrictions.enablePasswordVerifier", + "Xenoforo.predefinedSalt"}; + for (String deprecatedPath : deprecatedProperties) { + if (configuration.contains(deprecatedPath)) { + return true; + } + } + return false; + } + + // -------- + // Specific migrations + // -------- + + /** + * Check if {@code Email.mailText} is present and move it to the Email.html file if it doesn't exist yet. + * + * @param configuration The file configuration to verify + * @param dataFolder The plugin data folder + * @return True if a migration has been completed, false otherwise + */ + private static boolean performMailTextToFileMigration(FileConfiguration configuration, File dataFolder) { + final String oldSettingPath = "Email.mailText"; + if (!configuration.contains(oldSettingPath)) { + return false; + } + + 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(mailText); + } catch (IOException 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/java/fr/xephi/authme/settings/custom/NewSetting.java b/src/main/java/fr/xephi/authme/settings/custom/NewSetting.java deleted file mode 100644 index 6f9abdc44..000000000 --- a/src/main/java/fr/xephi/authme/settings/custom/NewSetting.java +++ /dev/null @@ -1,156 +0,0 @@ -package fr.xephi.authme.settings.custom; - -import com.google.common.annotations.VisibleForTesting; -import fr.xephi.authme.ConsoleLogger; -import fr.xephi.authme.settings.domain.Property; -import fr.xephi.authme.settings.propertymap.PropertyMap; -import fr.xephi.authme.util.CollectionUtils; -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.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -/** - * The new settings manager. - */ -public class NewSetting { - - private File file; - private FileConfiguration configuration; - - /** - * Constructor. - * Loads the file as YAML and checks its integrity. - * - * @param configuration The configuration to interact with - * @param file The configuration file - */ - public NewSetting(FileConfiguration configuration, File file) { - this.configuration = configuration; - this.file = file; - - // TODO ljacqu 20160109: Ensure that save() works as desired (i.e. that it always produces valid YAML) - // and then uncomment the lines below. Once this is uncommented, the checks in the old Settings.java should - // be removed as we should check to rewrite the config.yml file only at one place - // -------- - // PropertyMap propertyMap = SettingsFieldRetriever.getAllPropertyFields(); - // if (!containsAllSettings(propertyMap)) { - // save(propertyMap); - // } - } - - /** - * Constructor for testing purposes, allowing more options. - * - * @param configuration The FileConfiguration object to use - * @param file The file to write to - * @param propertyMap The property map whose properties should be verified for presence, or null to skip this - */ - @VisibleForTesting - NewSetting(FileConfiguration configuration, File file, PropertyMap propertyMap) { - this.configuration = configuration; - this.file = file; - - if (propertyMap != null && !containsAllSettings(propertyMap)) { - save(propertyMap); - } - } - - /** - * Get the given property from the configuration. - * - * @param property The property to retrieve - * @param The property's type - * @return The property's value - */ - public T getProperty(Property property) { - return property.getFromFile(configuration); - } - - public void save() { - save(SettingsFieldRetriever.getAllPropertyFields()); - } - - public void save(PropertyMap propertyMap) { - try (FileWriter writer = new FileWriter(file)) { - writer.write(""); - - // Contains all but the last node of the setting, e.g. [DataSource, mysql] for "DataSource.mysql.username" - List currentPath = new ArrayList<>(); - for (Map.Entry, String[]> entry : propertyMap.entrySet()) { - Property property = entry.getKey(); - - // Handle properties - List propertyPath = Arrays.asList(property.getPath().split("\\.")); - List commonPathParts = CollectionUtils.filterCommonStart( - currentPath, propertyPath.subList(0, propertyPath.size() - 1)); - List newPathParts = CollectionUtils.getRange(propertyPath, commonPathParts.size()); - - if (commonPathParts.isEmpty()) { - writer.append("\n"); - } - - int indentationLevel = commonPathParts.size(); - if (newPathParts.size() > 1) { - for (String path : newPathParts.subList(0, newPathParts.size() - 1)) { - writer.append("\n") - .append(indent(indentationLevel)) - .append(path) - .append(": "); - ++indentationLevel; - } - } - for (String comment : entry.getValue()) { - writer.append("\n") - .append(indent(indentationLevel)) - .append("# ") - .append(comment); - } - writer.append("\n") - .append(indent(indentationLevel)) - .append(CollectionUtils.getRange(newPathParts, newPathParts.size() - 1).get(0)) - .append(": "); - - List yamlLines = property.formatValueAsYaml(configuration); - String delim = ""; - for (String yamlLine : yamlLines) { - writer.append(delim).append(yamlLine); - delim = "\n" + indent(indentationLevel); - } - - currentPath = propertyPath.subList(0, propertyPath.size() - 1); - } - writer.flush(); - writer.close(); - } catch (IOException e) { - ConsoleLogger.showError("Could not save config file - " + StringUtils.formatException(e)); - ConsoleLogger.writeStackTrace(e); - } - } - - @VisibleForTesting - boolean containsAllSettings(PropertyMap propertyMap) { - for (Property property : propertyMap.keySet()) { - if (!property.isPresent(configuration)) { - return false; - } - } - return true; - } - - private static String indent(int level) { - // YAML uses indentation of 4 spaces - StringBuilder sb = new StringBuilder(level * 4); - for (int i = 0; i < level; ++i) { - sb.append(" "); - } - return sb.toString(); - } - -} diff --git a/src/main/java/fr/xephi/authme/settings/domain/EnumPropertyType.java b/src/main/java/fr/xephi/authme/settings/domain/EnumPropertyType.java index ed184bb7d..d38d0649d 100644 --- a/src/main/java/fr/xephi/authme/settings/domain/EnumPropertyType.java +++ b/src/main/java/fr/xephi/authme/settings/domain/EnumPropertyType.java @@ -1,13 +1,11 @@ package fr.xephi.authme.settings.domain; import org.bukkit.configuration.file.FileConfiguration; - -import java.util.List; - -import static java.util.Arrays.asList; +import org.yaml.snakeyaml.Yaml; /** * Enum property type. + * * @param The enum class */ class EnumPropertyType> extends PropertyType { @@ -28,17 +26,17 @@ class EnumPropertyType> extends PropertyType { return mappedValue != null ? mappedValue : property.getDefaultValue(); } - @Override - protected List asYaml(E value) { - return asList("'" + value + "'"); - } - @Override public boolean contains(Property property, FileConfiguration configuration) { return super.contains(property, configuration) && mapToEnum(configuration.getString(property.getPath())) != null; } + @Override + public String toYaml(E value, Yaml simpleYaml, Yaml singleQuoteYaml) { + return singleQuoteYaml.dump(value.name()); + } + private E mapToEnum(String value) { for (E entry : clazz.getEnumConstants()) { if (entry.name().equalsIgnoreCase(value)) { diff --git a/src/main/java/fr/xephi/authme/settings/domain/Property.java b/src/main/java/fr/xephi/authme/settings/domain/Property.java index 6b15b5d3b..f9637a7be 100644 --- a/src/main/java/fr/xephi/authme/settings/domain/Property.java +++ b/src/main/java/fr/xephi/authme/settings/domain/Property.java @@ -1,13 +1,14 @@ package fr.xephi.authme.settings.domain; import org.bukkit.configuration.file.FileConfiguration; +import org.yaml.snakeyaml.Yaml; import java.util.Arrays; import java.util.List; import java.util.Objects; /** - * Properties (i.e. a setting that is read from the config.yml file). + * Property class, representing a setting that is read from the config.yml file. */ public class Property { @@ -22,15 +23,43 @@ public class Property { this.defaultValue = defaultValue; } + /** + * Create a new property. See also {@link #newProperty(PropertyType, String, Object[])} for lists and + * {@link #newProperty(Class, String, Enum)}. + * + * @param type The property type + * @param path The property's path + * @param defaultValue The default value + * @param The type of the property + * @return The created property + */ public static Property newProperty(PropertyType type, String path, T defaultValue) { return new Property<>(type, path, defaultValue); } + /** + * Create a new list property. + * + * @param type The list type of the property + * @param path The property's path + * @param defaultValues The default value's items + * @param The list type + * @return The created list property + */ @SafeVarargs public static Property> newProperty(PropertyType> type, String path, U... defaultValues) { return new Property<>(type, path, Arrays.asList(defaultValues)); } + /** + * Create a new enum property. + * + * @param clazz The enum class + * @param path The property's path + * @param defaultValue The default value + * @param The enum type + * @return The created enum property + */ public static > Property newProperty(Class clazz, String path, E defaultValue) { return new Property<>(new EnumPropertyType<>(clazz), path, defaultValue); } @@ -53,9 +82,8 @@ public class Property { // ----- // Hooks to the PropertyType methods // ----- - /** - * Get the property value from the given configuration. + * Get the property value from the given configuration – guaranteed to never return null. * * @param configuration The configuration to read the value from * @return The value, or default if not present @@ -64,16 +92,6 @@ public class Property { return type.getFromFile(this, configuration); } - /** - * Format the property value as YAML. - * - * @param configuration The configuration to read the value from - * @return The property value as YAML - */ - public List formatValueAsYaml(FileConfiguration configuration) { - return type.asYaml(this, configuration); - } - /** * Return whether or not the given configuration file contains the property. * @@ -84,10 +102,21 @@ public class Property { return type.contains(this, configuration); } + /** + * Format the property's value as YAML. + * + * @param configuration The file configuration + * @param simpleYaml YAML object (default) + * @param singleQuoteYaml YAML object using single quotes + * @return The generated YAML + */ + public String toYaml(FileConfiguration configuration, Yaml simpleYaml, Yaml singleQuoteYaml) { + return type.toYaml(getFromFile(configuration), simpleYaml, singleQuoteYaml); + } + // ----- // Trivial getters // ----- - /** * Return the default value of the property. * diff --git a/src/main/java/fr/xephi/authme/settings/domain/PropertyType.java b/src/main/java/fr/xephi/authme/settings/domain/PropertyType.java index dc1975bba..28a505cfb 100644 --- a/src/main/java/fr/xephi/authme/settings/domain/PropertyType.java +++ b/src/main/java/fr/xephi/authme/settings/domain/PropertyType.java @@ -1,12 +1,10 @@ package fr.xephi.authme.settings.domain; import org.bukkit.configuration.file.FileConfiguration; +import org.yaml.snakeyaml.Yaml; -import java.util.ArrayList; import java.util.List; -import static java.util.Arrays.asList; - /** * Handles a certain property type and provides type-specific functionality. * @@ -16,7 +14,6 @@ import static java.util.Arrays.asList; public abstract class PropertyType { public static final PropertyType BOOLEAN = new BooleanProperty(); - public static final PropertyType DOUBLE = new DoubleProperty(); public static final PropertyType INTEGER = new IntegerProperty(); public static final PropertyType STRING = new StringProperty(); public static final PropertyType> STRING_LIST = new StringListProperty(); @@ -30,17 +27,6 @@ public abstract class PropertyType { */ public abstract T getFromFile(Property property, FileConfiguration configuration); - /** - * Return the property's value (or its default) as YAML. - * - * @param property The property to transform - * @param configuration The YAML configuration to read from - * @return The read value or its default in YAML format - */ - public List asYaml(Property property, FileConfiguration configuration) { - return asYaml(getFromFile(property, configuration)); - } - /** * Return whether the property is present in the given configuration. * @@ -53,12 +39,16 @@ public abstract class PropertyType { } /** - * Transform the given value to YAML. + * Format the value as YAML. * - * @param value The value to transform - * @return The value as YAML + * @param value The value to export + * @param simpleYaml YAML object (default) + * @param singleQuoteYaml YAML object set to use single quotes + * @return The generated YAML */ - protected abstract List asYaml(T value); + public String toYaml(T value, Yaml simpleYaml, Yaml singleQuoteYaml) { + return simpleYaml.dump(value); + } /** @@ -69,26 +59,6 @@ public abstract class PropertyType { public Boolean getFromFile(Property property, FileConfiguration configuration) { return configuration.getBoolean(property.getPath(), property.getDefaultValue()); } - - @Override - protected List asYaml(Boolean value) { - return asList(value ? "true" : "false"); - } - } - - /** - * Double property. - */ - private static final class DoubleProperty extends PropertyType { - @Override - public Double getFromFile(Property property, FileConfiguration configuration) { - return configuration.getDouble(property.getPath(), property.getDefaultValue()); - } - - @Override - protected List asYaml(Double value) { - return asList(String.valueOf(value)); - } } /** @@ -99,11 +69,6 @@ public abstract class PropertyType { public Integer getFromFile(Property property, FileConfiguration configuration) { return configuration.getInt(property.getPath(), property.getDefaultValue()); } - - @Override - protected List asYaml(Integer value) { - return asList(String.valueOf(value)); - } } /** @@ -114,15 +79,9 @@ public abstract class PropertyType { public String getFromFile(Property property, FileConfiguration configuration) { return configuration.getString(property.getPath(), property.getDefaultValue()); } - @Override - protected List asYaml(String value) { - return asList(toYamlLiteral(value)); - } - - public static String toYamlLiteral(String str) { - // TODO: Need to handle new lines properly - return "'" + str.replace("'", "''") + "'"; + public String toYaml(String value, Yaml simpleYaml, Yaml singleQuoteYaml) { + return singleQuoteYaml.dump(value); } } @@ -139,23 +98,18 @@ public abstract class PropertyType { } @Override - protected List asYaml(List value) { - if (value.isEmpty()) { - return asList("[]"); - } - - List resultLines = new ArrayList<>(); - resultLines.add(""); // add - for (String entry : value) { - // TODO: StringProperty#toYamlLiteral will return List... - resultLines.add(" - " + StringProperty.toYamlLiteral(entry)); - } - return resultLines; + public boolean contains(Property> property, FileConfiguration configuration) { + return configuration.contains(property.getPath()) && configuration.isList(property.getPath()); } @Override - public boolean contains(Property> property, FileConfiguration configuration) { - return configuration.contains(property.getPath()) && configuration.isList(property.getPath()); + public String toYaml(List value, Yaml simpleYaml, Yaml singleQuoteYaml) { + String yaml = singleQuoteYaml.dump(value); + // If the property is a non-empty list we need to append a new line because it will be + // something like the following, which requires a new line: + // - 'item 1' + // - 'second item in list' + return value.isEmpty() ? yaml : "\n" + yaml; } } diff --git a/src/main/java/fr/xephi/authme/settings/custom/BackupSettings.java b/src/main/java/fr/xephi/authme/settings/properties/BackupSettings.java similarity index 95% rename from src/main/java/fr/xephi/authme/settings/custom/BackupSettings.java rename to src/main/java/fr/xephi/authme/settings/properties/BackupSettings.java index f2e78931b..32c439db2 100644 --- a/src/main/java/fr/xephi/authme/settings/custom/BackupSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/BackupSettings.java @@ -1,4 +1,4 @@ -package fr.xephi.authme.settings.custom; +package fr.xephi.authme.settings.properties; import fr.xephi.authme.settings.domain.Comment; import fr.xephi.authme.settings.domain.Property; diff --git a/src/main/java/fr/xephi/authme/settings/custom/ConverterSettings.java b/src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java similarity index 96% rename from src/main/java/fr/xephi/authme/settings/custom/ConverterSettings.java rename to src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java index f32cf7483..9dc4ad60a 100644 --- a/src/main/java/fr/xephi/authme/settings/custom/ConverterSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/ConverterSettings.java @@ -1,4 +1,4 @@ -package fr.xephi.authme.settings.custom; +package fr.xephi.authme.settings.properties; import fr.xephi.authme.settings.domain.Comment; import fr.xephi.authme.settings.domain.Property; diff --git a/src/main/java/fr/xephi/authme/settings/custom/DatabaseSettings.java b/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java similarity index 99% rename from src/main/java/fr/xephi/authme/settings/custom/DatabaseSettings.java rename to src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java index 84f0b7085..49ddc597b 100644 --- a/src/main/java/fr/xephi/authme/settings/custom/DatabaseSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java @@ -1,4 +1,4 @@ -package fr.xephi.authme.settings.custom; +package fr.xephi.authme.settings.properties; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.settings.domain.Comment; diff --git a/src/main/java/fr/xephi/authme/settings/custom/EmailSettings.java b/src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java similarity index 98% rename from src/main/java/fr/xephi/authme/settings/custom/EmailSettings.java rename to src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java index 683143652..7b7ee3cef 100644 --- a/src/main/java/fr/xephi/authme/settings/custom/EmailSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/EmailSettings.java @@ -1,4 +1,4 @@ -package fr.xephi.authme.settings.custom; +package fr.xephi.authme.settings.properties; import fr.xephi.authme.settings.domain.Comment; import fr.xephi.authme.settings.domain.Property; diff --git a/src/main/java/fr/xephi/authme/settings/custom/HooksSettings.java b/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java similarity index 98% rename from src/main/java/fr/xephi/authme/settings/custom/HooksSettings.java rename to src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java index af0458ac4..b20d28686 100644 --- a/src/main/java/fr/xephi/authme/settings/custom/HooksSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/HooksSettings.java @@ -1,4 +1,4 @@ -package fr.xephi.authme.settings.custom; +package fr.xephi.authme.settings.properties; import fr.xephi.authme.settings.domain.Comment; import fr.xephi.authme.settings.domain.Property; diff --git a/src/main/java/fr/xephi/authme/settings/custom/PluginSettings.java b/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java similarity index 98% rename from src/main/java/fr/xephi/authme/settings/custom/PluginSettings.java rename to src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java index e35076db2..6fea29fb0 100644 --- a/src/main/java/fr/xephi/authme/settings/custom/PluginSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java @@ -1,4 +1,4 @@ -package fr.xephi.authme.settings.custom; +package fr.xephi.authme.settings.properties; import fr.xephi.authme.settings.domain.Comment; import fr.xephi.authme.settings.domain.Property; diff --git a/src/main/java/fr/xephi/authme/settings/custom/ProtectionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java similarity index 97% rename from src/main/java/fr/xephi/authme/settings/custom/ProtectionSettings.java rename to src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java index 2582c277f..f5a51215e 100644 --- a/src/main/java/fr/xephi/authme/settings/custom/ProtectionSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java @@ -1,4 +1,4 @@ -package fr.xephi.authme.settings.custom; +package fr.xephi.authme.settings.properties; import fr.xephi.authme.settings.domain.Comment; import fr.xephi.authme.settings.domain.Property; diff --git a/src/main/java/fr/xephi/authme/settings/custom/PurgeSettings.java b/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java similarity index 98% rename from src/main/java/fr/xephi/authme/settings/custom/PurgeSettings.java rename to src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java index 5fcc139d7..b4de9abfc 100644 --- a/src/main/java/fr/xephi/authme/settings/custom/PurgeSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java @@ -1,4 +1,4 @@ -package fr.xephi.authme.settings.custom; +package fr.xephi.authme.settings.properties; import fr.xephi.authme.settings.domain.Comment; import fr.xephi.authme.settings.domain.Property; diff --git a/src/main/java/fr/xephi/authme/settings/custom/RegistrationSettings.java b/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java similarity index 99% rename from src/main/java/fr/xephi/authme/settings/custom/RegistrationSettings.java rename to src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java index 7e8301456..129e4ab97 100644 --- a/src/main/java/fr/xephi/authme/settings/custom/RegistrationSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java @@ -1,4 +1,4 @@ -package fr.xephi.authme.settings.custom; +package fr.xephi.authme.settings.properties; import fr.xephi.authme.settings.domain.Comment; import fr.xephi.authme.settings.domain.Property; diff --git a/src/main/java/fr/xephi/authme/settings/custom/RestrictionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java similarity index 94% rename from src/main/java/fr/xephi/authme/settings/custom/RestrictionSettings.java rename to src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java index 32b68586d..386e130c0 100644 --- a/src/main/java/fr/xephi/authme/settings/custom/RestrictionSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java @@ -1,4 +1,4 @@ -package fr.xephi.authme.settings.custom; +package fr.xephi.authme.settings.properties; import fr.xephi.authme.settings.domain.Comment; import fr.xephi.authme.settings.domain.Property; @@ -18,13 +18,19 @@ public class RestrictionSettings implements SettingsClass { public static final Property ALLOW_CHAT = newProperty("settings.restrictions.allowChat", false); + @Comment({ + "Allow unlogged users to use all the commands if registration is not forced!", + "WARNING: use this only if you need it!)"}) + public static final Property ALLOW_ALL_COMMANDS_IF_REGISTRATION_IS_OPTIONAL = + newProperty("settings.restrictions.allowAllCommandsIfRegistrationIsOptional", false); + @Comment("Allowed commands for unauthenticated players") public static final Property> ALLOW_COMMANDS = newProperty(PropertyType.STRING_LIST, "settings.restrictions.allowCommands", "login", "register", "l", "reg", "email", "captcha"); @Comment("Max number of allowed registrations per IP") - // TODO ljacqu 20160109: If 0 == unlimited, add this fact ot the comment + // TODO ljacqu 20160109: If 0 == unlimited, add this fact to the comment public static final Property MAX_REGISTRATION_PER_IP = newProperty("settings.restrictions.maxRegPerIp", 1); diff --git a/src/main/java/fr/xephi/authme/settings/custom/SecuritySettings.java b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java similarity index 95% rename from src/main/java/fr/xephi/authme/settings/custom/SecuritySettings.java rename to src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java index 94e33258c..6a2ace715 100644 --- a/src/main/java/fr/xephi/authme/settings/custom/SecuritySettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java @@ -1,4 +1,4 @@ -package fr.xephi.authme.settings.custom; +package fr.xephi.authme.settings.properties; import fr.xephi.authme.security.HashAlgorithm; import fr.xephi.authme.settings.domain.Comment; @@ -30,6 +30,10 @@ public class SecuritySettings implements SettingsClass { public static final Property REMOVE_PASSWORD_FROM_CONSOLE = newProperty("Security.console.removePassword", true); + @Comment("Copy AuthMe log output in a separate file as well?") + public static final Property USE_LOGGING = + newProperty("Security.console.logConsole", true); + @Comment("Player need to put a captcha when he fails too lot the password") public static final Property USE_CAPTCHA = newProperty("Security.captcha.useCaptcha", false); diff --git a/src/main/java/fr/xephi/authme/settings/custom/SettingsFieldRetriever.java b/src/main/java/fr/xephi/authme/settings/properties/SettingsFieldRetriever.java similarity index 80% rename from src/main/java/fr/xephi/authme/settings/custom/SettingsFieldRetriever.java rename to src/main/java/fr/xephi/authme/settings/properties/SettingsFieldRetriever.java index 74b722e8f..cedd358ee 100644 --- a/src/main/java/fr/xephi/authme/settings/custom/SettingsFieldRetriever.java +++ b/src/main/java/fr/xephi/authme/settings/properties/SettingsFieldRetriever.java @@ -1,4 +1,4 @@ -package fr.xephi.authme.settings.custom; +package fr.xephi.authme.settings.properties; import fr.xephi.authme.settings.domain.Comment; import fr.xephi.authme.settings.domain.Property; @@ -12,9 +12,10 @@ import java.util.Arrays; import java.util.List; /** - * Utility class responsible for the retrieval of all {@link Property} fields via reflections. + * Utility class responsible for retrieving all {@link Property} fields + * from {@link SettingsClass} implementations via reflection. */ -final class SettingsFieldRetriever { +public final class SettingsFieldRetriever { /** The classes to scan for properties. */ private static final List> CONFIGURATION_CLASSES = Arrays.asList( @@ -37,7 +38,7 @@ final class SettingsFieldRetriever { for (Class clazz : CONFIGURATION_CLASSES) { Field[] declaredFields = clazz.getDeclaredFields(); for (Field field : declaredFields) { - Property property = getFieldIfRelevant(field); + Property property = getPropertyField(field); if (property != null) { properties.put(property, getCommentsForField(field)); } @@ -53,7 +54,13 @@ final class SettingsFieldRetriever { return new String[0]; } - private static Property getFieldIfRelevant(Field field) { + /** + * Return the given field's value if it is a static {@link Property}. + * + * @param field The field's value to return + * @return The property the field defines, or null if not applicable + */ + private static Property getPropertyField(Field field) { field.setAccessible(true); if (field.isAccessible() && Property.class.equals(field.getType()) && Modifier.isStatic(field.getModifiers())) { try { diff --git a/src/main/java/fr/xephi/authme/settings/propertymap/PropertyMapComparator.java b/src/main/java/fr/xephi/authme/settings/propertymap/PropertyMapComparator.java index e646fb61d..77f5f60ee 100644 --- a/src/main/java/fr/xephi/authme/settings/propertymap/PropertyMapComparator.java +++ b/src/main/java/fr/xephi/authme/settings/propertymap/PropertyMapComparator.java @@ -31,9 +31,4 @@ final class PropertyMapComparator implements Comparator { return Node.compare(parent, p1.getPath(), p2.getPath()); } - @Override - public boolean equals(Object obj) { - return this == obj; - } - } diff --git a/src/main/java/fr/xephi/authme/util/CollectionUtils.java b/src/main/java/fr/xephi/authme/util/CollectionUtils.java index 13077547c..6ec617a2c 100644 --- a/src/main/java/fr/xephi/authme/util/CollectionUtils.java +++ b/src/main/java/fr/xephi/authme/util/CollectionUtils.java @@ -52,11 +52,12 @@ public final class CollectionUtils { } /** - * @param element - * @param coll Collection - * @return boolean Boolean + * Null-safe way to check whether a collection is empty or not. + * + * @param coll The collection to verify + * @return True if the collection is null or empty, false otherwise */ - public static boolean isEmpty(Collection coll) { + public static boolean isEmpty(Collection coll) { return coll == null || coll.isEmpty(); } diff --git a/src/main/java/fr/xephi/authme/util/GeoLiteAPI.java b/src/main/java/fr/xephi/authme/util/GeoLiteAPI.java index ebd0deb31..7f519a779 100644 --- a/src/main/java/fr/xephi/authme/util/GeoLiteAPI.java +++ b/src/main/java/fr/xephi/authme/util/GeoLiteAPI.java @@ -36,7 +36,7 @@ public class GeoLiteAPI { plugin.getLogger().info(LICENSE); return true; } catch (IOException e) { - // TODO ljacqu 20151123: Log the exception instead of just swallowing it + ConsoleLogger.logException("Could not find/download GeoLiteAPI", e); return false; } } @@ -63,7 +63,7 @@ public class GeoLiteAPI { output.close(); input.close(); } catch (IOException e) { - ConsoleLogger.writeStackTrace(e); + ConsoleLogger.logException("Could not download GeoLiteAPI", e); } } }); diff --git a/src/main/java/fr/xephi/authme/util/StringUtils.java b/src/main/java/fr/xephi/authme/util/StringUtils.java index 9ae0ec0fd..377dd9f75 100644 --- a/src/main/java/fr/xephi/authme/util/StringUtils.java +++ b/src/main/java/fr/xephi/authme/util/StringUtils.java @@ -4,6 +4,7 @@ import net.ricecode.similarity.LevenshteinDistanceStrategy; import net.ricecode.similarity.StringSimilarityService; import net.ricecode.similarity.StringSimilarityServiceImpl; +import java.io.File; import java.util.Arrays; /** @@ -37,12 +38,12 @@ public final class StringUtils { } /** - * Returns whether the given string contains any of the provided elements. + * Return whether the given string contains any of the provided elements. * - * @param str the string to analyze - * @param pieces the items to check the string for + * @param str The string to analyze + * @param pieces The items to check the string for * - * @return true if the string contains at least one of the items + * @return True if the string contains at least one of the items */ public static boolean containsAny(String str, String... pieces) { if (str == null) { @@ -60,21 +61,21 @@ public final class StringUtils { * Null-safe method for checking whether a string is empty. Note that the string * is trimmed, so this method also considers a string with whitespace as empty. * - * @param str the string to verify + * @param str The string to verify * - * @return true if the string is empty, false otherwise + * @return True if the string is empty, false otherwise */ public static boolean isEmpty(String str) { return str == null || str.trim().isEmpty(); } /** - * Joins a list of elements into a single string with the specified delimiter. + * Join a list of elements into a single string with the specified delimiter. * - * @param delimiter the delimiter to use - * @param elements the elements to join + * @param delimiter The delimiter to use + * @param elements The elements to join * - * @return a new String that is composed of the elements separated by the delimiter + * @return A new String that is composed of the elements separated by the delimiter */ public static String join(String delimiter, Iterable elements) { if (delimiter == null) { @@ -95,12 +96,12 @@ public final class StringUtils { } /** - * Joins a list of elements into a single string with the specified delimiter. + * Join a list of elements into a single string with the specified delimiter. * - * @param delimiter the delimiter to use - * @param elements the elements to join + * @param delimiter The delimiter to use + * @param elements The elements to join * - * @return a new String that is composed of the elements separated by the delimiter + * @return A new String that is composed of the elements separated by the delimiter */ public static String join(String delimiter, String... elements) { return join(delimiter, Arrays.asList(elements)); @@ -117,4 +118,15 @@ public final class StringUtils { return "[" + th.getClass().getSimpleName() + "]: " + th.getMessage(); } + /** + * Construct a file path from the given elements, i.e. separate the given elements by the file separator. + * + * @param elements The elements to create a path with + * + * @return The created path + */ + public static String makePath(String... elements) { + return join(File.separator, elements); + } + } diff --git a/src/main/java/fr/xephi/authme/util/Utils.java b/src/main/java/fr/xephi/authme/util/Utils.java index 58d296bfa..f12c8b341 100644 --- a/src/main/java/fr/xephi/authme/util/Utils.java +++ b/src/main/java/fr/xephi/authme/util/Utils.java @@ -7,8 +7,10 @@ import fr.xephi.authme.cache.limbo.LimboCache; import fr.xephi.authme.cache.limbo.LimboPlayer; import fr.xephi.authme.events.AuthMeTeleportEvent; import fr.xephi.authme.permission.PermissionsManager; +import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.EmailSettings; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; @@ -19,6 +21,7 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.List; /** * Utility class for various operations used in the codebase. @@ -254,6 +257,30 @@ public final class Utils { } } + public static boolean isEmailCorrect(String email, NewSetting settings) { + if (!email.contains("@") || "your@email.com".equalsIgnoreCase(email)) { + return false; + } + final String emailDomain = email.split("@")[1]; + + List whitelist = settings.getProperty(EmailSettings.DOMAIN_WHITELIST); + if (!CollectionUtils.isEmpty(whitelist)) { + return containsIgnoreCase(whitelist, emailDomain); + } + + List blacklist = settings.getProperty(EmailSettings.DOMAIN_BLACKLIST); + return CollectionUtils.isEmpty(blacklist) || !containsIgnoreCase(blacklist, emailDomain); + } + + private static boolean containsIgnoreCase(Collection coll, String needle) { + for (String entry : coll) { + if (entry.equalsIgnoreCase(needle)) { + return true; + } + } + return false; + } + /** */ public enum GroupType { diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index d2e0a9e74..c9cdbfe64 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -67,6 +67,9 @@ settings: # Care that this feature blocks also all the commands not # listed in the list below. allowChat: false + # WARNING: use this only if you need it! + # Allow unlogged users to use all the commands if registration is not forced! + allowAllCommandsIfRegistrationIsOptional: false # Commands allowed when a player is not authenticated allowCommands: - /login @@ -305,6 +308,8 @@ Security: noConsoleSpam: false # Replace passwords in the console when player type a command like /login removePassword: true + # Copy AuthMe log output in a separate file as well? + logConsole: true captcha: # Player need to put a captcha when he fails too lot the password useCaptcha: false diff --git a/src/main/resources/messages/messages_hu.yml b/src/main/resources/messages/messages_hu.yml index f52e4f2b9..099179f80 100644 --- a/src/main/resources/messages/messages_hu.yml +++ b/src/main/resources/messages/messages_hu.yml @@ -4,56 +4,56 @@ registered: '&aSikeres regisztráció. Üdvözöllek!' user_regged: '&cJátékosnév már regisztrálva' login_msg: '&cKérlek jelentkezz be: "/login jelszó"' not_logged_in: '&cNem vagy bejelentkezve!' -logout: '&cSikeresen kijelentkeztél' -usage_log: '&cBejelentkezés: /login jelszó' -unknown_user: User is not in database -reg_voluntarily: Regisztrálhatod beceneved a szerveren a következö parancsal "/register jelszó jelszó" -reg_disabled: '&cRegisztráció letiltva' -no_perm: '&cNincs engedélyed' -usage_reg: '&cHasználat: /register jelszó jelszóújra' -password_error_nick: '&fYou can''t use your name as password' -password_error_unsafe: '&fYou can''t use unsafe passwords' +logout: '&cSikeresen kijelentkeztél!' +usage_log: '&cBejelentkezés: /login ' +unknown_user: '&cA kért felhasználó nem telálható az adatbázisban!' +reg_voluntarily: Regisztrálhatod magad a szerveren a következö parancsal "/register " +reg_disabled: '&cRegisztráció letiltva!' +no_perm: '&cNincs jogod ehhez!' +usage_reg: '&cHasználat: /register ' +password_error_nick: '&cNem használhatod a felhasználóneved jelszónak, kérlek válassz másikat...' +password_error_unsafe: '&cA választott jelszó nem biztonságos, kérlek válassz másikat...' unregistered: '&cRegisztráció sikeresen törölve!' -same_nick: Ezen a játékosnéven már játszanak -valid_session: '&cSession login' +same_nick: 'Ezzel a játékosnévvel már játszanak a szerveren.' +valid_session: '&2A hálózati kapcsolat újraépítése megtörtént.' pwd_changed: '&cJelszó cserélve!' -reload: Beálítások és adatbázis újratöltve! -timeout: Bejelentkezési idötúllépés -error: Hiba lépett fel; Lépj kapcsolatba a tulajjal' +reload: 'Beálítások és adatbázis újratöltve!' +timeout: 'Bejelentkezési időtúllépés!' +error: 'Hiba lépett fel; Lépj kapcsolatba a tulajjal' logged_in: '&cMár be vagy jelentkezve!' -login: '&aSikeresen Beléptél! Üdvözöllek!!!' -wrong_pwd: '&4Hibás jelszó' -user_unknown: '&cJátékosnév nem regisztrált' +login: '&aSikeresen beléptél!' +wrong_pwd: '&4Hibás jelszó!' +user_unknown: '&cEz a felhasználó nincs regisztrálva!' reg_msg: '&cKérlek Regisztrálj: "/register jelszó jelszóújra"' -reg_email_msg: '&cPlease register with "/register "' -unsafe_spawn: A kilépési helyzeted nem biztonságos, teleportálás a kezdö Spawnra. -max_reg: Csak egy karakterrel Registrálhatsz!!! -password_error: A jelszó nem illik össze -invalid_session: Session Dataes doesnt corrispond Plaese wait the end of session -pass_len: A jelszavad nem éri el a minimális hosszat -vb_nonActiv: Your Account isent Activated yet check your Emails! -usage_changepassword: 'használat: /changepassword régiJelszó újJelszó' -name_len: '&cYour nickname is too Short or too long' -regex: '&cYour nickname contains illegal characters. Allowed chars: REG_EX' -add_email: '&cPlease add your email with : /email add yourEmail confirmEmail' -recovery_email: '&cForgot your password? Please use /email recovery ' -usage_captcha: '&cUsage: /captcha ' -wrong_captcha: '&cWrong Captcha, please use : /captcha THE_CAPTCHA' -valid_captcha: '&cYour captcha is valid !' -kick_forvip: '&cA VIP Player join the full server!' -kick_fullserver: '&cThe server is actually full, Sorry!' -usage_email_add: '&fUsage: /email add ' -usage_email_change: '&fUsage: /email change ' -usage_email_recovery: '&fUsage: /email recovery ' -new_email_invalid: '[AuthMe] New email invalid!' -old_email_invalid: '[AuthMe] Old email invalid!' -email_invalid: '[AuthMe] Invalid Email' -email_added: '[AuthMe] Email Added !' -email_confirm: '[AuthMe] Confirm your Email !' -email_changed: '[AuthMe] Email Change !' -email_send: '[AuthMe] Recovery Email Send !' -country_banned: 'Your country is banned from this server' -antibot_auto_enabled: '[AuthMe] AntiBotMod automatically enabled due to massive connections!' -antibot_auto_disabled: '[AuthMe] AntiBotMod automatically disabled after %m Minutes, hope invasion stopped' -kick_antibot: 'AntiBot protection mode is enabled! You have to wait some minutes before joining the server.' -email_exists: '&cA recovery email was already sent! You can discard it and send a new one using the command below:' +reg_email_msg: '&cKérlek regisztrálj: "/register "' +unsafe_spawn: 'A kilépési helyzeted nem biztonságos, teleportálás a Spawnra.' +max_reg: 'Csak egy karakterrel registrálhatsz!' +password_error: 'A két jelszó nem egyezik!' +invalid_session: '&cAz IP címed megváltozott és a hálózati kapcsolatod lejárt. Kapcsolódj újra.' +pass_len: 'A jelszavad nem éri el a minimális hosszúságot!' +vb_nonActiv: '&cA felhasználód aktiválása még nem történt meg, ellenőrizd a leveleid!' +usage_changepassword: 'Használat: /changepassword <új Jelszó>' +name_len: '&4A felhasználó neved túl hosszú, vagy túl rövid! Válassz másikat!' +regex: '&4A felhasználóneved nem használható karaktereket tartalmaz. Elfogadott karakterek: REG_EX' +add_email: '&3Kérlek add hozzá a felhasználódhoz az email címedet "/email add "' +recovery_email: '&3Ha elfelejtetted a jelszavad, használd az: "/email recovery "' +usage_captcha: '&3A bejelentkezéshez CAPTCHA szükséges, kérem használd a következő parancsot "/captcha "' +wrong_captcha: '&cHibás captcha, kérlek írd be a következő parancsot "/captcha THE_CAPTCHA" a chat-be!' +valid_captcha: '&2Captcha sikeresen feloldva!' +kick_forvip: '&3VIP játékos csatlakozott a szerverhez!' +kick_fullserver: '&4A szerver megtelt, próbálj csatlakozni később!' +usage_email_add: '&cHasználat: /email add ' +usage_email_change: '&cHasználat: /email change <új Email>' +usage_email_recovery: '&cHasználat: /email recovery ' +new_email_invalid: '&cHibás az új email cím, próbáld újra!' +old_email_invalid: '&cHibás a régi email cím, próbáld újra!' +email_invalid: '&cHibás az email cím, próbáld újra!' +email_added: '&2Az email címed rögzítése sikeresen megtörtént!' +email_confirm: '&cKérlek ellenőrízd az email címedet!' +email_changed: '&2Az email cím cseréje sikeresen megtörtént!' +email_send: '&2A jelszó visszaállításhoz szükséges emailt elküldtük! Ellenőrízd a leveleidet!' +email_exists: '&cA visszaállító emailt elküldtük! Hiba esetén újkérheted az alábbi parancs segítségével:' +country_banned: '&4Az országod tiltólistán van ezen a szerveren!' +antibot_auto_enabled: '&4[AntiBot] Az AntiBot védelem bekapcsolt a nagy számú hálózati kapcsolat miatt!' +antibot_auto_disabled: '&2[AntiBot] Az AntiBot kikapcsol %m múlva!' +kick_antibot: 'Az AntiBot védelem bekapcsolva! Kérünk várj pár másodpercet a csatlakozáshoz.' 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/command/CommandServiceTest.java b/src/test/java/fr/xephi/authme/command/CommandServiceTest.java index e3e67e3ed..4ebd4c8a9 100644 --- a/src/test/java/fr/xephi/authme/command/CommandServiceTest.java +++ b/src/test/java/fr/xephi/authme/command/CommandServiceTest.java @@ -8,8 +8,8 @@ import fr.xephi.authme.output.Messages; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.process.Management; import fr.xephi.authme.security.PasswordSecurity; -import fr.xephi.authme.settings.custom.NewSetting; -import fr.xephi.authme.settings.custom.SecuritySettings; +import fr.xephi.authme.settings.NewSetting; +import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.settings.domain.Property; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -18,6 +18,7 @@ import org.junit.Ignore; import org.junit.Test; import org.mockito.ArgumentCaptor; +import java.io.File; import java.util.Arrays; import java.util.List; @@ -95,19 +96,6 @@ public class CommandServiceTest { verify(commandMapper).mapPartsToCommand(sender, commandParts); } - @Test - @Ignore - public void shouldRunTaskInAsync() { - // given - Runnable runnable = mock(Runnable.class); - - // when - commandService.runTaskAsynchronously(runnable); - - // then - // TODO ljacqu 20151226: AuthMe#getServer() is final, i.e. not mockable - } - @Test public void shouldGetDataSource() { // given @@ -193,10 +181,40 @@ public class CommandServiceTest { given(settings.getProperty(property)).willReturn(7); // when - int result = settings.getProperty(property); + int result = commandService.getProperty(property); // then assertThat(result, equalTo(7)); verify(settings).getProperty(property); } + + @Test + public void shouldReloadMessages() { + // given + File file = new File("some/bogus-file.test"); + + // when + commandService.reloadMessages(file); + + // then + verify(messages).reload(file); + } + + @Test + public void shouldReturnSettings() { + // given/when + NewSetting result = commandService.getSettings(); + + // then + assertThat(result, equalTo(settings)); + } + + @Test + public void shouldReturnAuthMe() { + // given/when + AuthMe result = commandService.getAuthMe(); + + // then + assertThat(result, equalTo(authMe)); + } } diff --git a/src/test/java/fr/xephi/authme/command/executable/captcha/CaptchaCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/captcha/CaptchaCommandTest.java index c33b13ef3..b3b54a1c4 100644 --- a/src/test/java/fr/xephi/authme/command/executable/captcha/CaptchaCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/captcha/CaptchaCommandTest.java @@ -5,7 +5,7 @@ import fr.xephi.authme.command.CommandService; import fr.xephi.authme.command.ExecutableCommand; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; -import fr.xephi.authme.settings.custom.SecuritySettings; +import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.util.WrapperMock; import org.bukkit.command.BlockCommandSender; import org.bukkit.command.CommandSender; diff --git a/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java index d00a4e37d..3936e5069 100644 --- a/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/changepassword/ChangePasswordCommandTest.java @@ -4,8 +4,8 @@ import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.output.MessageKey; -import fr.xephi.authme.settings.custom.RestrictionSettings; -import fr.xephi.authme.settings.custom.SecuritySettings; +import fr.xephi.authme.settings.properties.RestrictionSettings; +import fr.xephi.authme.settings.properties.SecuritySettings; import fr.xephi.authme.task.ChangePasswordTask; import fr.xephi.authme.util.WrapperMock; import org.bukkit.Server; diff --git a/src/test/java/fr/xephi/authme/command/executable/email/AddEmailCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/email/AddEmailCommandTest.java index 931c9c4eb..f1e55787f 100644 --- a/src/test/java/fr/xephi/authme/command/executable/email/AddEmailCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/email/AddEmailCommandTest.java @@ -2,6 +2,7 @@ package fr.xephi.authme.command.executable.email; import fr.xephi.authme.command.CommandService; import fr.xephi.authme.process.Management; +import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.util.WrapperMock; import org.bukkit.command.BlockCommandSender; import org.bukkit.command.CommandSender; @@ -51,6 +52,8 @@ public class AddEmailCommandTest { AddEmailCommand command = new AddEmailCommand(); Management management = mock(Management.class); given(commandService.getManagement()).willReturn(management); + NewSetting settings = mock(NewSetting.class); + given(commandService.getSettings()).willReturn(settings); // when command.executeCommand(sender, Arrays.asList("mail@example", "mail@example"), commandService); diff --git a/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java b/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java index cb9c85012..45e4d4240 100644 --- a/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java +++ b/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java @@ -6,7 +6,6 @@ import fr.xephi.authme.command.FoundResultStatus; import fr.xephi.authme.command.TestCommandsUtil; import fr.xephi.authme.permission.PermissionsManager; import fr.xephi.authme.permission.PlayerPermission; -import fr.xephi.authme.settings.Settings; import fr.xephi.authme.util.WrapperMock; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; @@ -40,22 +39,22 @@ import static org.mockito.Mockito.mock; */ public class HelpProviderTest { + private static final String HELP_HEADER = "Help"; + private static Set commands; private HelpProvider helpProvider; private PermissionsManager permissionsManager; private CommandSender sender; - private static Set commands; @BeforeClass public static void setUpCommands() { WrapperMock.createInstance(); - Settings.helpHeader = "Help"; commands = TestCommandsUtil.generateCommands(); } @Before public void setUpHelpProvider() { permissionsManager = mock(PermissionsManager.class); - helpProvider = new HelpProvider(permissionsManager); + helpProvider = new HelpProvider(permissionsManager, HELP_HEADER); sender = mock(CommandSender.class); } @@ -70,7 +69,7 @@ public class HelpProviderTest { // then assertThat(lines, hasSize(5)); - assertThat(lines.get(0), containsString(Settings.helpHeader + " HELP")); + assertThat(lines.get(0), containsString(HELP_HEADER + " HELP")); assertThat(removeColors(lines.get(1)), containsString("Command: /authme login ")); assertThat(removeColors(lines.get(2)), containsString("Short description: login cmd")); assertThat(removeColors(lines.get(3)), equalTo("Detailed description:")); @@ -88,7 +87,7 @@ public class HelpProviderTest { // then assertThat(lines, hasSize(4)); - assertThat(lines.get(0), containsString(Settings.helpHeader + " HELP")); + assertThat(lines.get(0), containsString(HELP_HEADER + " HELP")); assertThat(removeColors(lines.get(1)), equalTo("Arguments:")); assertThat(removeColors(lines.get(2)), containsString("password: 'password' argument description")); assertThat(removeColors(lines.get(3)), containsString("confirmation: 'confirmation' argument description")); @@ -279,7 +278,7 @@ public class HelpProviderTest { // then assertThat(lines, hasSize(2)); - assertThat(lines.get(0), containsString(Settings.helpHeader + " HELP")); + assertThat(lines.get(0), containsString(HELP_HEADER + " HELP")); assertThat(removeColors(lines.get(1)), containsString("Command: /authme register ")); } 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 343a56871..2d3e7b08f 100644 --- a/src/test/java/fr/xephi/authme/output/MessagesIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/output/MessagesIntegrationTest.java @@ -1,10 +1,11 @@ package fr.xephi.authme.output; -import fr.xephi.authme.settings.Settings; +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; @@ -28,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 @@ -35,17 +42,12 @@ public class MessagesIntegrationTest { */ @Before public void setUpMessages() { - WrapperMock.createInstance(); - - Settings.messagesLanguage = "en"; URL url = getClass().getClassLoader().getResource(YML_TEST_FILE); if (url == null) { throw new RuntimeException("File '" + YML_TEST_FILE + "' could not be loaded"); } - Settings.messageFile = new File(url.getFile()); - Settings.messagesLanguage = "en"; - messages = Messages.getInstance(); + messages = new Messages(new File(url.getFile())); } @Test diff --git a/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java b/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java index 97a6a9903..82eb77f13 100644 --- a/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java +++ b/src/test/java/fr/xephi/authme/process/email/AsyncAddEmailTest.java @@ -6,6 +6,7 @@ import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; +import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.util.WrapperMock; import org.bukkit.entity.Player; @@ -29,6 +30,7 @@ public class AsyncAddEmailTest { private Player player; private DataSource dataSource; private PlayerCache playerCache; + private NewSetting settings; @BeforeClass public static void setUp() { @@ -184,7 +186,8 @@ public class AsyncAddEmailTest { player = mock(Player.class); dataSource = mock(DataSource.class); playerCache = mock(PlayerCache.class); - return new AsyncAddEmail(authMe, player, email, dataSource, playerCache); + settings = mock(NewSetting.class); + return new AsyncAddEmail(authMe, player, email, dataSource, playerCache, settings); } } diff --git a/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java b/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java index 4faf14837..041b58d2a 100644 --- a/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java +++ b/src/test/java/fr/xephi/authme/process/email/AsyncChangeEmailTest.java @@ -6,6 +6,7 @@ import fr.xephi.authme.cache.auth.PlayerCache; import fr.xephi.authme.datasource.DataSource; import fr.xephi.authme.output.MessageKey; import fr.xephi.authme.output.Messages; +import fr.xephi.authme.settings.NewSetting; import fr.xephi.authme.settings.Settings; import fr.xephi.authme.util.WrapperMock; import org.bukkit.entity.Player; @@ -29,6 +30,7 @@ public class AsyncChangeEmailTest { private Messages messages; private PlayerCache playerCache; private DataSource dataSource; + private NewSetting settings; @BeforeClass public static void setUp() { @@ -221,6 +223,7 @@ public class AsyncChangeEmailTest { when(authMe.getMessages()).thenReturn(messages); playerCache = mock(PlayerCache.class); dataSource = mock(DataSource.class); - return new AsyncChangeEmail(player, authMe, oldEmail, newEmail, dataSource, playerCache); + settings = mock(NewSetting.class); + return new AsyncChangeEmail(player, authMe, oldEmail, newEmail, dataSource, playerCache, settings); } } diff --git a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java b/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java index cf15eb5f9..81fdebea3 100644 --- a/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java +++ b/src/test/java/fr/xephi/authme/security/PasswordSecurityTest.java @@ -11,7 +11,6 @@ import fr.xephi.authme.util.WrapperMock; import org.bukkit.event.Event; import org.bukkit.plugin.PluginManager; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.invocation.InvocationOnMock; @@ -25,7 +24,6 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; 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/settings/custom/ConfigFileConsistencyTest.java b/src/test/java/fr/xephi/authme/settings/ConfigFileConsistencyTest.java similarity index 86% rename from src/test/java/fr/xephi/authme/settings/custom/ConfigFileConsistencyTest.java rename to src/test/java/fr/xephi/authme/settings/ConfigFileConsistencyTest.java index d012a50a4..755dcc461 100644 --- a/src/test/java/fr/xephi/authme/settings/custom/ConfigFileConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/settings/ConfigFileConsistencyTest.java @@ -1,7 +1,7 @@ -package fr.xephi.authme.settings.custom; +package fr.xephi.authme.settings; -import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.settings.domain.Property; +import fr.xephi.authme.settings.properties.SettingsFieldRetriever; import fr.xephi.authme.settings.propertymap.PropertyMap; import fr.xephi.authme.util.StringUtils; import org.bukkit.configuration.MemorySection; @@ -34,16 +34,14 @@ public class ConfigFileConsistencyTest { // given URL url = this.getClass().getResource(CONFIG_FILE); File configFile = new File(url.getFile()); - NewSetting settings = new NewSetting(YamlConfiguration.loadConfiguration(configFile), new File("bogus"), null); + FileConfiguration configuration = YamlConfiguration.loadConfiguration(configFile); // when - boolean result = settings.containsAllSettings(SettingsFieldRetriever.getAllPropertyFields()); + boolean result = SettingsMigrationService.containsAllSettings( + configuration, SettingsFieldRetriever.getAllPropertyFields()); // then if (!result) { - FileConfiguration configuration = - (FileConfiguration) ReflectionTestUtils.getFieldValue(NewSetting.class, settings, "configuration"); - Set knownProperties = getAllKnownPropertyPaths(); List missingProperties = new ArrayList<>(); for (String path : knownProperties) { diff --git a/src/test/java/fr/xephi/authme/settings/custom/NewSettingIntegrationTest.java b/src/test/java/fr/xephi/authme/settings/NewSettingIntegrationTest.java similarity index 54% rename from src/test/java/fr/xephi/authme/settings/custom/NewSettingIntegrationTest.java rename to src/test/java/fr/xephi/authme/settings/NewSettingIntegrationTest.java index 73b80b020..646c18456 100644 --- a/src/test/java/fr/xephi/authme/settings/custom/NewSettingIntegrationTest.java +++ b/src/test/java/fr/xephi/authme/settings/NewSettingIntegrationTest.java @@ -1,11 +1,13 @@ -package fr.xephi.authme.settings.custom; +package fr.xephi.authme.settings; import com.google.common.collect.ImmutableMap; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.settings.domain.Property; +import fr.xephi.authme.settings.properties.TestConfiguration; +import fr.xephi.authme.settings.properties.TestEnum; import fr.xephi.authme.settings.propertymap.PropertyMap; +import fr.xephi.authme.util.WrapperMock; import org.bukkit.configuration.file.YamlConfiguration; -import org.junit.BeforeClass; import org.junit.Test; import java.io.File; @@ -13,8 +15,10 @@ import java.lang.reflect.Field; import java.net.URL; import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Map; +import static fr.xephi.authme.settings.domain.Property.newProperty; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeThat; @@ -28,40 +32,28 @@ public class NewSettingIntegrationTest { private static final String COMPLETE_FILE = "config-sample-values.yml"; /** File name of the sample config missing certain {@link TestConfiguration} values. */ private static final String INCOMPLETE_FILE = "config-incomplete-sample.yml"; + /** File name for testing difficult values. */ + private static final String DIFFICULT_FILE = "config-difficult-values.yml"; - private static PropertyMap propertyMap; - - @BeforeClass - public static void generatePropertyMap() { - propertyMap = new PropertyMap(); - for (Field field : TestConfiguration.class.getDeclaredFields()) { - Object fieldValue = ReflectionTestUtils.getFieldValue(TestConfiguration.class, null, field.getName()); - if (fieldValue instanceof Property) { - Property property = (Property) fieldValue; - String[] comments = new String[]{"Comment for '" + property.getPath() + "'"}; - propertyMap.put(property, comments); - } - } - } + private static PropertyMap propertyMap = generatePropertyMap(); @Test public void shouldLoadAndReadAllProperties() { // given YamlConfiguration configuration = YamlConfiguration.loadConfiguration(getConfigFile(COMPLETE_FILE)); File file = new File("unused"); - assumeThat(file.exists(), equalTo(false)); // when / then NewSetting settings = new NewSetting(configuration, file, propertyMap); Map, Object> expectedValues = ImmutableMap., Object>builder() .put(TestConfiguration.DURATION_IN_SECONDS, 22) .put(TestConfiguration.SYSTEM_NAME, "Custom sys name") - .put(TestConfiguration.RATIO_LIMIT, -4.1) + .put(TestConfiguration.RATIO_ORDER, TestEnum.FIRST) .put(TestConfiguration.RATIO_FIELDS, Arrays.asList("Australia", "Burundi", "Colombia")) .put(TestConfiguration.VERSION_NUMBER, 2492) .put(TestConfiguration.SKIP_BORING_FEATURES, false) .put(TestConfiguration.BORING_COLORS, Arrays.asList("beige", "gray")) - .put(TestConfiguration.DUST_LEVEL, 0.81) + .put(TestConfiguration.DUST_LEVEL, 2) .put(TestConfiguration.USE_COOL_FEATURES, true) .put(TestConfiguration.COOL_OPTIONS, Arrays.asList("Dinosaurs", "Explosions", "Big trucks")) .build(); @@ -89,12 +81,12 @@ public class NewSettingIntegrationTest { Map, Object> expectedValues = ImmutableMap., Object>builder() .put(TestConfiguration.DURATION_IN_SECONDS, 22) .put(TestConfiguration.SYSTEM_NAME, "[TestDefaultValue]") - .put(TestConfiguration.RATIO_LIMIT, 3.0) + .put(TestConfiguration.RATIO_ORDER, TestEnum.SECOND) .put(TestConfiguration.RATIO_FIELDS, Arrays.asList("Australia", "Burundi", "Colombia")) .put(TestConfiguration.VERSION_NUMBER, 32046) .put(TestConfiguration.SKIP_BORING_FEATURES, false) .put(TestConfiguration.BORING_COLORS, Collections.EMPTY_LIST) - .put(TestConfiguration.DUST_LEVEL, 0.2) + .put(TestConfiguration.DUST_LEVEL, -1) .put(TestConfiguration.USE_COOL_FEATURES, false) .put(TestConfiguration.COOL_OPTIONS, Arrays.asList("Dinosaurs", "Explosions", "Big trucks")) .build(); @@ -104,6 +96,62 @@ public class NewSettingIntegrationTest { } } + /** Verify that "difficult cases" such as apostrophes in strings etc. are handled properly. */ + @Test + public void shouldProperlyExportAnyValues() { + // given + File file = getConfigFile(DIFFICULT_FILE); + YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file); + assumeThat(configuration.contains(TestConfiguration.DUST_LEVEL.getPath()), equalTo(false)); + + // Additional string properties + List> additionalProperties = Arrays.asList( + newProperty("more.string1", "it's a text with some \\'apostrophes'"), + newProperty("more.string2", "\tthis one\nhas some\nnew '' lines-test") + ); + PropertyMap propertyMap = generatePropertyMap(); + for (Property property : additionalProperties) { + propertyMap.put(property, new String[0]); + } + + // when + new NewSetting(configuration, file, propertyMap); + // reload the file as settings should hav been rewritten + configuration = YamlConfiguration.loadConfiguration(file); + + // then + // assert that we won't rewrite the settings again! One rewrite should produce a valid, complete configuration + File unusedFile = new File("config-difficult-values.unused.yml"); + NewSetting settings = new NewSetting(configuration, unusedFile, propertyMap); + assertThat(unusedFile.exists(), equalTo(false)); + assertThat(configuration.contains(TestConfiguration.DUST_LEVEL.getPath()), equalTo(true)); + + Map, Object> expectedValues = ImmutableMap., Object>builder() + .put(TestConfiguration.DURATION_IN_SECONDS, 20) + .put(TestConfiguration.SYSTEM_NAME, "A 'test' name") + .put(TestConfiguration.RATIO_ORDER, TestEnum.FOURTH) + .put(TestConfiguration.RATIO_FIELDS, Arrays.asList("Australia\\", "\tBurundi'", "Colombia?\n''")) + .put(TestConfiguration.VERSION_NUMBER, -1337) + .put(TestConfiguration.SKIP_BORING_FEATURES, false) + .put(TestConfiguration.BORING_COLORS, Arrays.asList("it's a difficult string!", "gray\nwith new lines\n")) + .put(TestConfiguration.DUST_LEVEL, -1) + .put(TestConfiguration.USE_COOL_FEATURES, true) + .put(TestConfiguration.COOL_OPTIONS, Collections.EMPTY_LIST) + .put(additionalProperties.get(0), additionalProperties.get(0).getDefaultValue()) + .put(additionalProperties.get(1), additionalProperties.get(1).getDefaultValue()) + .build(); + for (Map.Entry, Object> entry : expectedValues.entrySet()) { + assertThat("Property '" + entry.getKey().getPath() + "' has expected value" + + entry.getValue() + " but found " + settings.getProperty(entry.getKey()), + settings.getProperty(entry.getKey()), equalTo(entry.getValue())); + } + } + + /** + * Return a {@link File} instance to an existing file in the target/test-classes folder. + * + * @return The generated File + */ private File getConfigFile(String file) { URL url = getClass().getClassLoader().getResource(file); if (url == null) { @@ -112,4 +160,23 @@ public class NewSettingIntegrationTest { return new File(url.getFile()); } + /** + * Generate a property map with all properties in {@link TestConfiguration}. + * + * @return The generated property map + */ + private static PropertyMap generatePropertyMap() { + WrapperMock.createInstance(); + PropertyMap propertyMap = new PropertyMap(); + for (Field field : TestConfiguration.class.getDeclaredFields()) { + Object fieldValue = ReflectionTestUtils.getFieldValue(TestConfiguration.class, null, field.getName()); + if (fieldValue instanceof Property) { + Property property = (Property) fieldValue; + String[] comments = new String[]{"Comment for '" + property.getPath() + "'"}; + propertyMap.put(property, comments); + } + } + return propertyMap; + } + } diff --git a/src/test/java/fr/xephi/authme/settings/custom/NewSettingTest.java b/src/test/java/fr/xephi/authme/settings/NewSettingTest.java similarity index 87% rename from src/test/java/fr/xephi/authme/settings/custom/NewSettingTest.java rename to src/test/java/fr/xephi/authme/settings/NewSettingTest.java index 64e213aed..8ae651289 100644 --- a/src/test/java/fr/xephi/authme/settings/custom/NewSettingTest.java +++ b/src/test/java/fr/xephi/authme/settings/NewSettingTest.java @@ -1,6 +1,9 @@ -package fr.xephi.authme.settings.custom; +package fr.xephi.authme.settings; import fr.xephi.authme.settings.domain.Property; +import fr.xephi.authme.settings.properties.TestConfiguration; +import fr.xephi.authme.settings.properties.TestEnum; +import fr.xephi.authme.settings.propertymap.PropertyMap; import org.bukkit.configuration.file.YamlConfiguration; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; @@ -35,15 +38,15 @@ public class NewSettingTest { setReturnValue(file, TestConfiguration.VERSION_NUMBER, 20); setReturnValue(file, TestConfiguration.SKIP_BORING_FEATURES, true); - setReturnValue(file, TestConfiguration.RATIO_LIMIT, 4.25); + setReturnValue(file, TestConfiguration.RATIO_ORDER, TestEnum.THIRD); setReturnValue(file, TestConfiguration.SYSTEM_NAME, "myTestSys"); // when / then - NewSetting settings = new NewSetting(file, new File("conf.txt"), null); + NewSetting settings = new NewSetting(file, new File("conf.txt"), (PropertyMap) null); assertThat(settings.getProperty(TestConfiguration.VERSION_NUMBER), equalTo(20)); assertThat(settings.getProperty(TestConfiguration.SKIP_BORING_FEATURES), equalTo(true)); - assertThat(settings.getProperty(TestConfiguration.RATIO_LIMIT), equalTo(4.25)); + assertThat(settings.getProperty(TestConfiguration.RATIO_ORDER), equalTo(TestEnum.THIRD)); assertThat(settings.getProperty(TestConfiguration.SYSTEM_NAME), equalTo("myTestSys")); assertDefaultValue(TestConfiguration.DURATION_IN_SECONDS, settings); @@ -58,8 +61,8 @@ public class NewSettingTest { when(config.getInt(eq(property.getPath()), anyInt())).thenReturn((Integer) value); } else if (value instanceof Boolean) { when(config.getBoolean(eq(property.getPath()), anyBoolean())).thenReturn((Boolean) value); - } else if (value instanceof Double) { - when(config.getDouble(eq(property.getPath()), anyDouble())).thenReturn((Double) value); + } else if (value instanceof Enum) { + when(config.getString(property.getPath())).thenReturn(((Enum) value).name()); } else { throw new UnsupportedOperationException("Value has unsupported type '" + (value == null ? "null" : value.getClass().getSimpleName()) + "'"); diff --git a/src/test/java/fr/xephi/authme/settings/domain/PropertyTypeTest.java b/src/test/java/fr/xephi/authme/settings/domain/PropertyTypeTest.java index 6dce2ad3e..df0120631 100644 --- a/src/test/java/fr/xephi/authme/settings/domain/PropertyTypeTest.java +++ b/src/test/java/fr/xephi/authme/settings/domain/PropertyTypeTest.java @@ -13,7 +13,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyDouble; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; @@ -33,8 +32,6 @@ public class PropertyTypeTest { when(configuration.getBoolean(eq("bool.path.test"), anyBoolean())).thenReturn(true); when(configuration.getBoolean(eq("bool.path.wrong"), anyBoolean())).thenAnswer(secondParameter()); - when(configuration.getDouble(eq("double.path.test"), anyDouble())).thenReturn(-6.4); - when(configuration.getDouble(eq("double.path.wrong"), anyDouble())).thenAnswer(secondParameter()); when(configuration.getInt(eq("int.path.test"), anyInt())).thenReturn(27); when(configuration.getInt(eq("int.path.wrong"), anyInt())).thenAnswer(secondParameter()); when(configuration.getString(eq("str.path.test"), anyString())).thenReturn("Test value"); @@ -69,31 +66,6 @@ public class PropertyTypeTest { assertThat(result, equalTo(true)); } - /* Double */ - @Test - public void shouldGetDoubleValue() { - // given - Property property = Property.newProperty(PropertyType.DOUBLE, "double.path.test", 3.8); - - // when - double result = property.getFromFile(configuration); - - // then - assertThat(result, equalTo(-6.4)); - } - - @Test - public void shouldGetDoubleDefault() { - // given - Property property = Property.newProperty(PropertyType.DOUBLE, "double.path.wrong", 12.0); - - // when - double result = property.getFromFile(configuration); - - // then - assertThat(result, equalTo(12.0)); - } - /* Integer */ @Test public void shouldGetIntValue() { diff --git a/src/test/java/fr/xephi/authme/settings/custom/SettingsClassConsistencyTest.java b/src/test/java/fr/xephi/authme/settings/properties/SettingsClassConsistencyTest.java similarity index 98% rename from src/test/java/fr/xephi/authme/settings/custom/SettingsClassConsistencyTest.java rename to src/test/java/fr/xephi/authme/settings/properties/SettingsClassConsistencyTest.java index a6b67763a..075103345 100644 --- a/src/test/java/fr/xephi/authme/settings/custom/SettingsClassConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/settings/properties/SettingsClassConsistencyTest.java @@ -1,4 +1,4 @@ -package fr.xephi.authme.settings.custom; +package fr.xephi.authme.settings.properties; import fr.xephi.authme.ReflectionTestUtils; import fr.xephi.authme.settings.domain.Property; @@ -25,7 +25,7 @@ import static org.junit.Assert.fail; */ public class SettingsClassConsistencyTest { - private static final String SETTINGS_FOLDER = "src/main/java/fr/xephi/authme/settings/custom"; + private static final String SETTINGS_FOLDER = "src/main/java/fr/xephi/authme/settings/properties"; private static List> classes; @BeforeClass diff --git a/src/test/java/fr/xephi/authme/settings/custom/TestConfiguration.java b/src/test/java/fr/xephi/authme/settings/properties/TestConfiguration.java similarity index 75% rename from src/test/java/fr/xephi/authme/settings/custom/TestConfiguration.java rename to src/test/java/fr/xephi/authme/settings/properties/TestConfiguration.java index 5f43262c7..dc02e9ccb 100644 --- a/src/test/java/fr/xephi/authme/settings/custom/TestConfiguration.java +++ b/src/test/java/fr/xephi/authme/settings/properties/TestConfiguration.java @@ -1,4 +1,4 @@ -package fr.xephi.authme.settings.custom; +package fr.xephi.authme.settings.properties; import fr.xephi.authme.settings.domain.Property; import fr.xephi.authme.settings.domain.PropertyType; @@ -11,7 +11,7 @@ import static fr.xephi.authme.settings.domain.Property.newProperty; /** * Sample properties for testing purposes. */ -class TestConfiguration implements SettingsClass { +public final class TestConfiguration implements SettingsClass { public static final Property DURATION_IN_SECONDS = newProperty("test.duration", 4); @@ -19,8 +19,8 @@ class TestConfiguration implements SettingsClass { public static final Property SYSTEM_NAME = newProperty("test.systemName", "[TestDefaultValue]"); - public static final Property RATIO_LIMIT = - newProperty(PropertyType.DOUBLE, "sample.ratio.limit", 3.0); + public static final Property RATIO_ORDER = + newProperty(TestEnum.class, "sample.ratio.order", TestEnum.SECOND); public static final Property> RATIO_FIELDS = newProperty(PropertyType.STRING_LIST, "sample.ratio.fields", "a", "b", "c"); @@ -34,8 +34,8 @@ class TestConfiguration implements SettingsClass { public static final Property> BORING_COLORS = newProperty(PropertyType.STRING_LIST, "features.boring.colors"); - public static final Property DUST_LEVEL = - newProperty(PropertyType.DOUBLE, "features.boring.dustLevel", 0.2); + public static final Property DUST_LEVEL = + newProperty(PropertyType.INTEGER, "features.boring.dustLevel", -1); public static final Property USE_COOL_FEATURES = newProperty("features.cool.enabled", false); @@ -43,4 +43,8 @@ class TestConfiguration implements SettingsClass { public static final Property> COOL_OPTIONS = newProperty(PropertyType.STRING_LIST, "features.cool.options", "Sparks", "Sprinkles"); + + private TestConfiguration() { + } + } diff --git a/src/test/java/fr/xephi/authme/settings/properties/TestEnum.java b/src/test/java/fr/xephi/authme/settings/properties/TestEnum.java new file mode 100644 index 000000000..e02d6825a --- /dev/null +++ b/src/test/java/fr/xephi/authme/settings/properties/TestEnum.java @@ -0,0 +1,16 @@ +package fr.xephi.authme.settings.properties; + +/** + * Test enum used in {@link TestConfiguration}. + */ +public enum TestEnum { + + FIRST, + + SECOND, + + THIRD, + + FOURTH + +} diff --git a/src/test/java/fr/xephi/authme/util/StringUtilsTest.java b/src/test/java/fr/xephi/authme/util/StringUtilsTest.java index 238c8bcef..6cc7799aa 100644 --- a/src/test/java/fr/xephi/authme/util/StringUtilsTest.java +++ b/src/test/java/fr/xephi/authme/util/StringUtilsTest.java @@ -2,6 +2,7 @@ package fr.xephi.authme.util; import org.junit.Test; +import java.io.File; import java.net.MalformedURLException; import java.util.Arrays; import java.util.List; @@ -135,4 +136,13 @@ public class StringUtilsTest { assertThat(StringUtils.getDifference("test", "bear"), equalTo(0.75)); assertThat(StringUtils.getDifference("test", "something"), greaterThan(0.88)); } + + @Test + public void shouldConstructPath() { + // given/when + String result = StringUtils.makePath("path", "to", "test-file.txt"); + + // then + assertThat(result, equalTo("path" + File.separator + "to" + File.separator + "test-file.txt")); + } } diff --git a/src/test/java/fr/xephi/authme/util/UtilsTest.java b/src/test/java/fr/xephi/authme/util/UtilsTest.java index 40769ae53..3ed7ee626 100644 --- a/src/test/java/fr/xephi/authme/util/UtilsTest.java +++ b/src/test/java/fr/xephi/authme/util/UtilsTest.java @@ -1,16 +1,22 @@ 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; import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.EmailSettings; import org.bukkit.entity.Player; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; @@ -35,6 +41,7 @@ public class UtilsTest { public static void setUpMocks() { WrapperMock wrapperMock = WrapperMock.createInstance(); authMeMock = wrapperMock.getAuthMe(); + ConsoleLoggerTestInitializer.setupLogger(); } @Before @@ -64,6 +71,10 @@ public class UtilsTest { } @Test + @Ignore + // TODO ljacqu 20160206: Running this test with all others results in an error + // because Utils is used elsewhere. The AuthMe field is set in a static block + // so creating the WrapperMock here will have no effect public void shouldNotAddToNormalGroupIfPermManagerIsNull() { // given Settings.isPermissionCheckEnabled = true; @@ -92,8 +103,96 @@ public class UtilsTest { assertThat(players, hasSize(2)); } + // ---------------- + // Tests for Utils#isEmailCorrect() + // ---------------- + @Test + public void shouldAcceptEmailWithEmptyLists() { + // given + NewSetting settings = mock(NewSetting.class); + given(settings.getProperty(EmailSettings.DOMAIN_WHITELIST)).willReturn(Collections.EMPTY_LIST); + given(settings.getProperty(EmailSettings.DOMAIN_BLACKLIST)).willReturn(Collections.EMPTY_LIST); + + // when + boolean result = Utils.isEmailCorrect("test@example.org", settings); + + // then + assertThat(result, equalTo(true)); + } + + @Test + public void shouldAcceptEmailWithWhitelist() { + // given + NewSetting settings = mock(NewSetting.class); + given(settings.getProperty(EmailSettings.DOMAIN_WHITELIST)) + .willReturn(Arrays.asList("domain.tld", "example.com")); + given(settings.getProperty(EmailSettings.DOMAIN_BLACKLIST)).willReturn(Collections.EMPTY_LIST); + + // when + boolean result = Utils.isEmailCorrect("TesT@Example.com", settings); + + // then + assertThat(result, equalTo(true)); + } + + @Test + public void shouldRejectEmailNotInWhitelist() { + // given + NewSetting settings = mock(NewSetting.class); + given(settings.getProperty(EmailSettings.DOMAIN_WHITELIST)) + .willReturn(Arrays.asList("domain.tld", "example.com")); + given(settings.getProperty(EmailSettings.DOMAIN_BLACKLIST)).willReturn(Collections.EMPTY_LIST); + + // when + boolean result = Utils.isEmailCorrect("email@other-domain.abc", settings); + + // then + assertThat(result, equalTo(false)); + } + + @Test + public void shouldAcceptEmailNotInBlacklist() { + // given + NewSetting settings = mock(NewSetting.class); + given(settings.getProperty(EmailSettings.DOMAIN_WHITELIST)).willReturn(Collections.EMPTY_LIST); + given(settings.getProperty(EmailSettings.DOMAIN_BLACKLIST)) + .willReturn(Arrays.asList("Example.org", "a-test-name.tld")); + + // when + boolean result = Utils.isEmailCorrect("sample@valid-name.tld", settings); + + // then + assertThat(result, equalTo(true)); + } + + @Test + public void shouldRejectEmailInBlacklist() { + // given + NewSetting settings = mock(NewSetting.class); + given(settings.getProperty(EmailSettings.DOMAIN_WHITELIST)).willReturn(Collections.EMPTY_LIST); + given(settings.getProperty(EmailSettings.DOMAIN_BLACKLIST)) + .willReturn(Arrays.asList("Example.org", "a-test-name.tld")); + + // when + boolean result = Utils.isEmailCorrect("sample@a-Test-name.tld", settings); + + // then + assertThat(result, equalTo(false)); + } + + @Test + public void shouldRejectInvalidEmail() { + // given/when/then + assertThat(Utils.isEmailCorrect("invalidinput", mock(NewSetting.class)), equalTo(false)); + } + + @Test + public void shouldRejectDefaultEmail() { + // given/when/then + assertThat(Utils.isEmailCorrect("your@email.com", mock(NewSetting.class)), equalTo(false)); + } + // Note: This method is used through reflections - @SuppressWarnings("unused") public static Player[] onlinePlayersImpl() { return new Player[]{ mock(Player.class), mock(Player.class) diff --git a/src/test/resources/config-difficult-values.yml b/src/test/resources/config-difficult-values.yml new file mode 100644 index 000000000..29bc0e371 --- /dev/null +++ b/src/test/resources/config-difficult-values.yml @@ -0,0 +1,29 @@ +# Test config file with some "difficult" values + +test: + duration: 20.102 + systemName: 'A ''test'' name' +sample: + ratio: + order: Fourth + fields: + - Australia\ + - ' Burundi''' + - 'Colombia? + + ''''' + # The last element above represents "Colombia?\n''" +version: -1337 +features: + boring: + # YAML allows both "yes"/"no" and "true"/"false" for expressing booleans + skip: no + colors: + - 'it''s a difficult string!' + - | + gray + with new lines + # dustLevel: 8 <-- missing property triggering rewrite + cool: + enabled: yes + options: [] diff --git a/src/test/resources/config-incomplete-sample.yml b/src/test/resources/config-incomplete-sample.yml index a29879720..65990e84d 100644 --- a/src/test/resources/config-incomplete-sample.yml +++ b/src/test/resources/config-incomplete-sample.yml @@ -6,7 +6,7 @@ test: # systemName: 'Custom sys name' sample: ratio: -# limit: 3.0 +# order: 'THIRD' fields: - 'Australia' - 'Burundi' @@ -18,7 +18,7 @@ features: # colors: # - 'beige' # - 'gray' -# dustLevel: 0.81 +# dustLevel: 1 cool: # enabled: true options: diff --git a/src/test/resources/config-sample-values.yml b/src/test/resources/config-sample-values.yml index 1bd99d771..72cdeb790 100644 --- a/src/test/resources/config-sample-values.yml +++ b/src/test/resources/config-sample-values.yml @@ -6,7 +6,7 @@ test: systemName: 'Custom sys name' sample: ratio: - limit: -4.1 + order: 'first' fields: - 'Australia' - 'Burundi' @@ -18,7 +18,7 @@ features: colors: - 'beige' - 'gray' - dustLevel: 0.81 + dustLevel: 2 cool: enabled: true options: