11package ratismal .drivebackup .config ;
22
3+ import java .nio .charset .StandardCharsets ;
4+ import java .security .MessageDigest ;
5+ import java .security .NoSuchAlgorithmException ;
6+ import java .util .Arrays ;
7+ import java .util .HashSet ;
38import java .util .List ;
9+ import java .util .Set ;
410
511import org .apache .commons .lang .ObjectUtils ;
612import org .bukkit .command .CommandSender ;
713import org .bukkit .configuration .file .FileConfiguration ;
14+ import org .bukkit .configuration .file .YamlConfiguration ;
815
916import ratismal .drivebackup .config .ConfigParser .Config ;
17+ import ratismal .drivebackup .util .CustomConfig ;
1018import ratismal .drivebackup .util .Logger ;
1119import ratismal .drivebackup .plugin .DriveBackup ;
1220import ratismal .drivebackup .util .MessageUtil ;
1321
22+ import java .io .InputStream ;
23+ import java .io .InputStreamReader ;
24+
1425public class ConfigMigrator {
1526 private static final String DEFAULT_TIMEZONE_STRING = "-00:00" ;
1627
1728 // ConfigMigrator is called before localization is parsed, since ConfigMigrator
1829 // may change the intl file. Therefore, we just hardcode any messages.
1930 private static final String MIGRATING_MESSAGE = "Automatically migrating config to version <version>" ;
31+ private static final String UPDATING_INTL_MESSAGE = "Automatically updating intl config to latest version (no user modifications detected)" ;
32+
33+ private static final Set <String > KNOWN_DEFAULT_INTL_HASHES = new HashSet <>(Arrays .asList (
34+ "a4318fb0fcfc9508d66cb5e07221f287914c750ba0fc8388fd47535bf747a36b" , // v1.8.1-
35+ "cb18f3edd7b8af5fd59d0d545adf23439e1c14686c79b62c03f953ace5b5642c" , // v1.8.0
36+ "240e14212dfd3d48d855e7bf387f9e444cbb33305ccb5b3820bf7e0ba7f62c26" , // v1.7.0
37+ "b2e9699dd99cc975ed938ddcf9b466e7c5348e8985c547547a1eba429db1644b" , // v1.6.2-3
38+ "f5c2f75a44569bde08020323b6045faf4aca0563b8f3472712bd68a53428e298" , // v1.6.1
39+ "cfccd3e5f28adb640fcb5717a6c43cac4be6c6927271157a10635a65bce54931" , // v1.6.0
40+ "0ce295bca8664327df64d88dcb1e58c94d0c30a8389729a499efee85801b969c" , // v1.5.4
41+ "8a452d3ea74753193a3113c4cc5b970c29662d2a08622407d08c8fda33df371a" , // v1.5.2-3
42+ "1d90033798544849264e7139b11f5d1faaf0c950f028c05057eb6580101c5f84" // v1.5.0-1
43+ ));
2044
2145 private FileConfiguration config ;
22- private FileConfiguration localizationConfig ;
46+ private CustomConfig localizationConfig ;
2347 private List <CommandSender > initiators ;
2448
25- public ConfigMigrator (FileConfiguration config , FileConfiguration localizationConfig ,
49+ public ConfigMigrator (FileConfiguration config , CustomConfig localizationConfig ,
2650 List <CommandSender > initiators ) {
2751 this .config = config ;
2852 this .localizationConfig = localizationConfig ;
@@ -31,7 +55,9 @@ public ConfigMigrator(FileConfiguration config, FileConfiguration localizationCo
3155
3256 public void migrate () {
3357 Logger logger = (input , placeholders ) -> MessageUtil .Builder ().mmText (input , placeholders ).to (initiators ).send ();
58+
3459 if (config .isSet ("version" ) && config .getInt ("version" ) >= Config .VERSION ) {
60+ fastTrackIntlConfig (logger );
3561 return ;
3662 }
3763 logger .log (MIGRATING_MESSAGE , "version" , String .valueOf (Config .VERSION ));
@@ -67,10 +93,83 @@ public void migrate() {
6793 migrateIntl ("messages.next-schedule-backup" , "next-schedule-backup" );
6894 migrateIntl ("messages.next-schedule-backup-format" , "next-schedule-backup-format" );
6995 migrateIntl ("messages.auto-backups-disabled" , "auto-backups-disabled" );
96+
97+ fastTrackIntlConfig (logger );
98+
7099 DriveBackup .getInstance ().saveConfig ();
71100 DriveBackup .getInstance ().saveIntlConfig ();
72101 }
73102
103+ /**
104+ * Checks if the intl config file matches a known default version hash.
105+ * If it does, automatically replaces it with the latest default version.
106+ *
107+ * @param logger the logger to use for messages
108+ */
109+ private void fastTrackIntlConfig (Logger logger ) {
110+ try {
111+ InputStream defIntlStream = DriveBackup .getInstance ().getResource ("intl.yml" );
112+
113+ if (defIntlStream == null ) {
114+ return ;
115+ }
116+
117+ FileConfiguration defaultIntl = YamlConfiguration .loadConfiguration (new InputStreamReader (defIntlStream , StandardCharsets .UTF_8 ));
118+
119+ String currentHash = calculateIntlHash (localizationConfig .getConfig ());
120+ String latestDefaultHash = calculateIntlHash (defaultIntl );
121+
122+ if (currentHash .equals (latestDefaultHash )) {
123+ return ;
124+ }
125+
126+ if (KNOWN_DEFAULT_INTL_HASHES .contains (currentHash )) {
127+ logger .log (UPDATING_INTL_MESSAGE );
128+
129+ DriveBackup .getInstance ().saveResource ("intl.yml" , true );
130+ }
131+ } catch (Exception e ) {
132+ logger .log ("Unable to migrate intl file to latest version" );
133+ e .printStackTrace ();
134+ }
135+ }
136+
137+ /**
138+ * Calculates a hash of the intl config content for comparison.
139+ *
140+ * @param config the config to hash
141+ * @return the SHA-256 hash as a hex string
142+ */
143+ private String calculateIntlHash (FileConfiguration config ) {
144+ try {
145+ StringBuilder content = new StringBuilder ();
146+
147+ java .util .TreeSet <String > sortedKeys = new java .util .TreeSet <>(config .getKeys (true ));
148+
149+ for (String key : sortedKeys ) {
150+ Object value = config .get (key );
151+ if (value != null && !config .isConfigurationSection (key )) {
152+ content .append (key ).append (":" ).append (value .toString ()).append ("\n " );
153+ }
154+ }
155+
156+ MessageDigest digest = MessageDigest .getInstance ("SHA-256" );
157+ byte [] hash = digest .digest (content .toString ().getBytes (StandardCharsets .UTF_8 ));
158+
159+ StringBuilder hexString = new StringBuilder ();
160+ for (byte b : hash ) {
161+ String hex = Integer .toHexString (0xff & b );
162+ if (hex .length () == 1 ) hexString .append ('0' );
163+ hexString .append (hex );
164+ }
165+
166+ return hexString .toString ();
167+ } catch (NoSuchAlgorithmException e ) {
168+ e .printStackTrace ();
169+ return "" ;
170+ }
171+ }
172+
74173 /**
75174 * Migrates a setting from the specified old path in the config
76175 * to the new path.
@@ -89,7 +188,8 @@ private void migrate(String oldPath, String newPath) {
89188 * @param newPath the new path
90189 */
91190 private void migrateIntl (String oldPath , String newPath ) {
92- localizationConfig .set (newPath , config .get (oldPath ));
191+ FileConfiguration intlConfig = localizationConfig .getConfig ();
192+ intlConfig .set (newPath , config .get (oldPath ));
93193 config .set (oldPath , null );
94194 }
95195}
0 commit comments