Dateien nach "src/main/java/zombie_striker/sr" hochladen
This commit is contained in:
731
src/main/java/zombie_striker/sr/Main.java
Normal file
731
src/main/java/zombie_striker/sr/Main.java
Normal file
@@ -0,0 +1,731 @@
|
||||
package me.zombie_striker.sr;
|
||||
|
||||
import com.jcraft.jsch.Channel;
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.JSch;
|
||||
import com.jcraft.jsch.Session;
|
||||
import org.apache.commons.net.ftp.FTP;
|
||||
import org.apache.commons.net.ftp.FTPClient;
|
||||
import org.apache.commons.net.ftp.FTPSClient;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.SocketException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
public class Main extends JavaPlugin {
|
||||
|
||||
private static List<String> exceptions = new ArrayList<String>();
|
||||
private static String prefix = "&6[&3ServerRestorer-Reborn&6]&8";
|
||||
private static String kickmessage = " Server wird auf den vorherigen Speicherstand zurückgesetzt. Bitte trete in wenigen Sekunden erneut bei.";
|
||||
BukkitTask br = null;
|
||||
private boolean saveTheConfig = false;
|
||||
private long lastSave = 0;
|
||||
private long timedist = 0;
|
||||
private File master = null;
|
||||
private File backups = null;
|
||||
private boolean saveServerJar = false;
|
||||
private boolean savePluiginJars = false;
|
||||
private boolean currentlySaving = false;
|
||||
private boolean automate = true;
|
||||
private boolean useFTP = false;
|
||||
private boolean useFTPS = false;
|
||||
private boolean useSFTP = false;
|
||||
private String serverFTP = "www.example.com";
|
||||
private String userFTP = "User";
|
||||
private String passwordFTP = "password";
|
||||
private int portFTP = 80;
|
||||
private String naming_format = "Backup-%date%";
|
||||
private SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
|
||||
private String removeFilePath = "";
|
||||
private long maxSaveSize = -1;
|
||||
private int maxSaveFiles = 1000;
|
||||
private boolean deleteZipOnFail = false;
|
||||
private boolean deleteZipOnFTP = false;
|
||||
|
||||
private int hourToSaveAt = -1;
|
||||
|
||||
private String separator = File.separator;
|
||||
|
||||
|
||||
|
||||
private int compression = Deflater.BEST_COMPRESSION;
|
||||
|
||||
public static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException {
|
||||
File destFile = new File(destinationDir, zipEntry.getName());
|
||||
|
||||
String destDirPath = destinationDir.getCanonicalPath();
|
||||
String destFilePath = destFile.getCanonicalPath();
|
||||
|
||||
if (!destFilePath.startsWith(destDirPath + File.separator)) {
|
||||
throw new IOException("Entry is outside of the target dir: " + zipEntry.getName());
|
||||
}
|
||||
|
||||
return destFile;
|
||||
}
|
||||
|
||||
private static boolean isExempt(String path) {
|
||||
path = path.toLowerCase().trim();
|
||||
for (String s : exceptions)
|
||||
if (path.endsWith(s.toLowerCase().trim()))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String humanReadableByteCount(long bytes, boolean si) {
|
||||
int unit = si ? 1000 : 1024;
|
||||
if (bytes < unit)
|
||||
return bytes + " B";
|
||||
int exp = (int) (Math.log(bytes) / Math.log(unit));
|
||||
String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i");
|
||||
return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre);
|
||||
}
|
||||
|
||||
public static long folderSize(File directory) {
|
||||
long length = 0;
|
||||
if(directory==null)return -1;
|
||||
|
||||
for (File file : directory.listFiles()) {
|
||||
if (file.isFile())
|
||||
length += file.length();
|
||||
else
|
||||
length += folderSize(file);
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
public static File firstFileModified(File dir) {
|
||||
File fl = dir;
|
||||
File[] files = fl.listFiles(new FileFilter() {
|
||||
public boolean accept(File file) {
|
||||
return file.isFile();
|
||||
}
|
||||
});
|
||||
long lastMod = Long.MAX_VALUE;
|
||||
File choice = null;
|
||||
for (File file : files) {
|
||||
if (file.lastModified() < lastMod) {
|
||||
choice = file;
|
||||
lastMod = file.lastModified();
|
||||
}
|
||||
}
|
||||
return choice;
|
||||
}
|
||||
|
||||
public File getMasterFolder() {
|
||||
return master;
|
||||
}
|
||||
|
||||
public File getBackupFolder() {
|
||||
return backups;
|
||||
}
|
||||
|
||||
public long a(String path, long def) {
|
||||
if (getConfig().contains(path))
|
||||
return getConfig().getLong(path);
|
||||
saveTheConfig = true;
|
||||
getConfig().set(path, def);
|
||||
return def;
|
||||
}
|
||||
public Object a(String path, Object def) {
|
||||
if (getConfig().contains(path))
|
||||
return getConfig().get(path);
|
||||
saveTheConfig = true;
|
||||
getConfig().set(path, def);
|
||||
return def;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void onEnable() {
|
||||
master = getDataFolder().getAbsoluteFile().getParentFile().getParentFile();
|
||||
String path = ((String) a("getBackupFileDirectory", ""));
|
||||
backups = new File((path.isEmpty() ? master.getPath() : path) + File.separator+"backups"+ File.separator);
|
||||
if (!backups.exists())
|
||||
backups.mkdirs();
|
||||
saveServerJar = (boolean) a("saveServerJar", false);
|
||||
savePluiginJars = (boolean) a("savePluginJars", false);
|
||||
|
||||
timedist = toTime((String) a("AutosaveDelay", "1D,0H"));
|
||||
lastSave = a("LastAutosave", 0L);
|
||||
|
||||
automate = (boolean) a("enableautoSaving", true);
|
||||
|
||||
naming_format = (String) a("FileNameFormat", naming_format);
|
||||
|
||||
String unPrefix = (String) a("prefix", "&6[&3ServerRestorer&6]&8");
|
||||
prefix = ChatColor.translateAlternateColorCodes('&', unPrefix);
|
||||
String kicky = (String) a("kickMessage", unPrefix + " Restoring server to previous save. Please rejoin in a few seconds.");
|
||||
kickmessage = ChatColor.translateAlternateColorCodes('&', kicky);
|
||||
|
||||
useFTP = (boolean) a("EnableFTP", false);
|
||||
useFTPS = (boolean) a("EnableFTPS", false);
|
||||
useSFTP = (boolean) a("EnableSFTP", false);
|
||||
serverFTP = (String) a("FTPAdress", serverFTP);
|
||||
portFTP = (int) a("FTPPort", portFTP);
|
||||
userFTP = (String) a("FTPUsername", userFTP);
|
||||
passwordFTP = (String) a("FTPPassword", passwordFTP);
|
||||
|
||||
|
||||
compression = (int) a("CompressionLevel_Max_9", compression);
|
||||
|
||||
removeFilePath = (String) a("FTP_Directory", removeFilePath);
|
||||
|
||||
hourToSaveAt = (int) a("AutoBackup-HourToBackup", hourToSaveAt);
|
||||
|
||||
if (!getConfig().contains("exceptions")) {
|
||||
exceptions.add("logs");
|
||||
exceptions.add("crash-reports");
|
||||
exceptions.add("backups");
|
||||
exceptions.add("dynmap");
|
||||
exceptions.add(".lock");
|
||||
exceptions.add("pixelprinter");
|
||||
}
|
||||
exceptions = (List<String>) a("exceptions", exceptions);
|
||||
|
||||
maxSaveSize = toByteSize((String) a("MaxSaveSize", "10G"));
|
||||
maxSaveFiles = (int) a("MaxFileSaved", 1000);
|
||||
|
||||
deleteZipOnFTP = (boolean) a("DeleteZipOnFTPTransfer", false);
|
||||
deleteZipOnFail = (boolean) a("DeleteZipIfFailed", false);
|
||||
separator = (String) a("FolderSeparator", separator);
|
||||
if (saveTheConfig)
|
||||
saveConfig();
|
||||
if (automate) {
|
||||
final JavaPlugin thi = this;
|
||||
br = new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Calendar calendar = GregorianCalendar.getInstance(); // creates a new calendar instance
|
||||
calendar.setTime(new Date()); // assigns calendar to given date
|
||||
int hour = calendar.get(Calendar.HOUR_OF_DAY);
|
||||
|
||||
if (System.currentTimeMillis() - lastSave >= timedist && (hourToSaveAt==-1 || hourToSaveAt == hour)) {
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
getConfig().set("LastAutosave", lastSave = (System.currentTimeMillis()-5000));
|
||||
save(Bukkit.getConsoleSender());
|
||||
saveConfig();
|
||||
}
|
||||
}.runTaskLater(thi, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}.runTaskTimerAsynchronously(this, 20, 20*60);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
|
||||
if (args.length == 1) {
|
||||
List<String> list = new ArrayList<>();
|
||||
String[] commands = new String[]{"disableAutoSaver", "enableAutoSaver", "restore", "save","stop", "toggleOptions"};
|
||||
for (String f : commands) {
|
||||
if (f.toLowerCase().startsWith(args[0].toLowerCase()))
|
||||
list.add(f);
|
||||
}
|
||||
return list;
|
||||
|
||||
}
|
||||
|
||||
if (args.length > 1) {
|
||||
if (args[0].equalsIgnoreCase("restore")) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (File f : getBackupFolder().listFiles()) {
|
||||
if (f.getName().toLowerCase().startsWith(args[1].toLowerCase()))
|
||||
list.add(f.getName());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
return super.onTabComplete(sender, command, alias, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!sender.hasPermission("serverrestorer.command")) {
|
||||
sender.sendMessage(prefix + ChatColor.RED + " You do not have permission to use this command.");
|
||||
return true;
|
||||
}
|
||||
if (args.length == 0) {
|
||||
sender.sendMessage(ChatColor.GOLD + "---===+Server Restorer+===---");
|
||||
sender.sendMessage("/sr save : Saves the server");
|
||||
sender.sendMessage("/sr stop : Stops creating a backup of the server");
|
||||
sender.sendMessage("/sr restore <backup> : Restores server to previous backup (automatically restarts)");
|
||||
sender.sendMessage("/sr enableAutoSaver [1H,6H,1D,7D] : Configure how long it takes to autosave");
|
||||
sender.sendMessage("/sr disableAutoSaver : Disables the autosaver");
|
||||
sender.sendMessage("/sr toggleOptions : TBD");
|
||||
return true;
|
||||
}
|
||||
if (args[0].equalsIgnoreCase("restore")) {
|
||||
if(true) {
|
||||
sender.sendMessage(prefix+ "Restore feature is temporarily disabled. Please load the files manually.");
|
||||
return true;
|
||||
}
|
||||
if (!sender.hasPermission("serverrestorer.restore")) {
|
||||
sender.sendMessage(prefix + ChatColor.RED + " You do not have permission to use this command.");
|
||||
return true;
|
||||
}
|
||||
if (currentlySaving) {
|
||||
sender.sendMessage(prefix + " The server is currently being saved. Please wait.");
|
||||
return true;
|
||||
}
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(prefix + " A valid backup file is required.");
|
||||
return true;
|
||||
}
|
||||
File backup = new File(getBackupFolder(), args[1]);
|
||||
if (!backup.exists()) {
|
||||
sender.sendMessage(prefix + " The file \"" + args[1] + "\" does not exist.");
|
||||
return true;
|
||||
}
|
||||
restore(backup);
|
||||
sender.sendMessage(prefix + " Restoration complete.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (args[0].equalsIgnoreCase("stop")) {
|
||||
if (!sender.hasPermission("serverrestorer.save")) {
|
||||
sender.sendMessage(prefix + ChatColor.RED + " You do not have permission to use this command.");
|
||||
return true;
|
||||
}
|
||||
if (currentlySaving) {
|
||||
currentlySaving=false;
|
||||
return true;
|
||||
}
|
||||
sender.sendMessage(prefix + " The server is not currently being saved.");
|
||||
return true;
|
||||
}
|
||||
if (args[0].equalsIgnoreCase("save")) {
|
||||
if (!sender.hasPermission("serverrestorer.save")) {
|
||||
sender.sendMessage(prefix + ChatColor.RED + " You do not have permission to use this command.");
|
||||
return true;
|
||||
}
|
||||
if (currentlySaving) {
|
||||
sender.sendMessage(prefix + " The server is currently being saved. Please wait.");
|
||||
return true;
|
||||
}
|
||||
save(sender);
|
||||
return true;
|
||||
}
|
||||
if (args[0].equalsIgnoreCase("disableAutoSaver")) {
|
||||
if (!sender.hasPermission("serverrestorer.save")) {
|
||||
sender.sendMessage(prefix + ChatColor.RED + " You do not have permission to use this command.");
|
||||
return true;
|
||||
}
|
||||
if (br != null)
|
||||
br.cancel();
|
||||
br = null;
|
||||
getConfig().set("enableautoSaving", false);
|
||||
saveConfig();
|
||||
sender.sendMessage(prefix + " Canceled delay.");
|
||||
}
|
||||
if (args[0].equalsIgnoreCase("enableAutoSaver")) {
|
||||
if (!sender.hasPermission("serverrestorer.save")) {
|
||||
sender.sendMessage(prefix + ChatColor.RED + " You do not have permission to use this command.");
|
||||
return true;
|
||||
}
|
||||
if (args.length == 1) {
|
||||
sender.sendMessage(prefix + " Please select a delay [E.G. 0.5H, 6H, 1D, 7D...]");
|
||||
return true;
|
||||
}
|
||||
String delay = args[1];
|
||||
getConfig().set("AutosaveDelay", delay);
|
||||
getConfig().set("enableautoSaving", true);
|
||||
saveConfig();
|
||||
if (br != null)
|
||||
br.cancel();
|
||||
br = null;
|
||||
br = new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (System.currentTimeMillis() - lastSave > timedist) {
|
||||
save(Bukkit.getConsoleSender());
|
||||
getConfig().set("LastAutosave", lastSave = System.currentTimeMillis());
|
||||
saveConfig();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}.runTaskTimerAsynchronously(this, 20, 20 * 60 * 30);
|
||||
|
||||
sender.sendMessage(prefix + " Set the delay to \"" + delay + "\".");
|
||||
}
|
||||
if (args[0].equalsIgnoreCase("toggleOptions")) {
|
||||
if (!sender.hasPermission("serverrestorer.save")) {
|
||||
sender.sendMessage(prefix + ChatColor.RED + " You do not have permission to use this command.");
|
||||
return true;
|
||||
}
|
||||
sender.sendMessage(prefix + " Coming soon !");
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void save(CommandSender sender) {
|
||||
currentlySaving = true;
|
||||
sender.sendMessage(prefix + " Starting to save directory. Please wait.");
|
||||
List<World> autosave = new ArrayList<>();
|
||||
for (World loaded : Bukkit.getWorlds()) {
|
||||
try {
|
||||
loaded.save();
|
||||
if (loaded.isAutoSave()) {
|
||||
autosave.add(loaded);
|
||||
loaded.setAutoSave(false);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
try {
|
||||
if(backups.listFiles().length > maxSaveFiles){
|
||||
for(int i = 0; i < backups.listFiles().length-maxSaveFiles; i++){
|
||||
File oldestBack = firstFileModified(backups);
|
||||
sender.sendMessage(prefix + ChatColor.RED + oldestBack.getName()
|
||||
+ ": File goes over max amount of files that can be saved.");
|
||||
oldestBack.delete();
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < Math.min(maxSaveFiles, backups.listFiles().length - 1); j++) {
|
||||
if (folderSize(backups) >= maxSaveSize) {
|
||||
File oldestBack = firstFileModified(backups);
|
||||
sender.sendMessage(prefix + ChatColor.RED + oldestBack.getName()
|
||||
+ ": The current save goes over the max savesize, and so the oldest file has been deleted. If you wish to save older backups, copy them to another location.");
|
||||
oldestBack.delete();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Error | Exception e) {
|
||||
}
|
||||
final long time = lastSave = System.currentTimeMillis();
|
||||
Date d = new Date(lastSave);
|
||||
File zipFile = new File(getBackupFolder(),
|
||||
naming_format.replaceAll("%date%", dateformat.format(d)) + ".zip");
|
||||
if (!zipFile.exists()) {
|
||||
zipFile.getParentFile().mkdirs();
|
||||
zipFile = new File(getBackupFolder(),
|
||||
naming_format.replaceAll("%date%", dateformat.format(d)) + ".zip");
|
||||
zipFile.createNewFile();
|
||||
}
|
||||
zipFolder(getMasterFolder().getPath(), zipFile.getPath());
|
||||
|
||||
long timeDif = (System.currentTimeMillis() - time) / 1000;
|
||||
String timeDifS = (((int) (timeDif / 60)) + "M, " + (timeDif % 60) + "S");
|
||||
|
||||
if(!currentlySaving){
|
||||
for (World world : autosave)
|
||||
world.setAutoSave(true);
|
||||
sender.sendMessage(prefix + " Backup canceled.");
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage(prefix + " Done! Backup took:" + timeDifS);
|
||||
File tempBackupCheck = new File(getMasterFolder(), "backups");
|
||||
sender.sendMessage(prefix + " Compressed server with size of "
|
||||
+ (humanReadableByteCount(folderSize(getMasterFolder())
|
||||
- (tempBackupCheck.exists() ? folderSize(tempBackupCheck) : 0), false))
|
||||
+ " to " + humanReadableByteCount(zipFile.length(), false));
|
||||
currentlySaving = false;
|
||||
for (World world : autosave)
|
||||
world.setAutoSave(true);
|
||||
if (useSFTP) {
|
||||
try {
|
||||
sender.sendMessage(prefix + " Starting SFTP Transfer");
|
||||
JSch jsch = new JSch();
|
||||
Session session = jsch.getSession(userFTP, serverFTP, portFTP);
|
||||
session.setConfig("PreferredAuthentications", "password");
|
||||
session.setPassword(passwordFTP);
|
||||
session.connect(1000 * 20);
|
||||
Channel channel = session.openChannel("sftp");
|
||||
ChannelSftp sftp = (ChannelSftp) channel;
|
||||
sftp.connect(1000 * 20);
|
||||
} catch (Exception | Error e) {
|
||||
sender.sendMessage(
|
||||
prefix + " FAILED TO SFTP TRANSFER FILE: " + zipFile.getName() + ". ERROR IN CONSOLE.");
|
||||
if (deleteZipOnFail)
|
||||
zipFile.delete();
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else if (useFTPS) {
|
||||
sender.sendMessage(prefix + " Starting FTPS Transfer");
|
||||
FileInputStream zipFileStream = new FileInputStream(zipFile);
|
||||
FTPSClient ftpClient = new FTPSClient();
|
||||
try {
|
||||
if (ftpClient.isConnected()) {
|
||||
sender.sendMessage(prefix + "FTPSClient was already connected. Disconnecting");
|
||||
ftpClient.logout();
|
||||
ftpClient.disconnect();
|
||||
ftpClient = new FTPSClient();
|
||||
}
|
||||
sendFTP(sender, zipFile, ftpClient, zipFileStream, removeFilePath);
|
||||
if (deleteZipOnFTP)
|
||||
zipFile.delete();
|
||||
} catch (Exception | Error e) {
|
||||
sender.sendMessage(
|
||||
prefix + " FAILED TO FTPS TRANSFER FILE: " + zipFile.getName() + ". ERROR IN CONSOLE.");
|
||||
if (deleteZipOnFail)
|
||||
zipFile.delete();
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (ftpClient.isConnected()) {
|
||||
sender.sendMessage(prefix + "Disconnecting");
|
||||
ftpClient.logout();
|
||||
ftpClient.disconnect();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
} else if (useFTP) {
|
||||
sender.sendMessage(prefix + " Starting FTP Transfer");
|
||||
FileInputStream zipFileStream = new FileInputStream(zipFile);
|
||||
FTPClient ftpClient = new FTPClient();
|
||||
try {
|
||||
if (ftpClient.isConnected()) {
|
||||
sender.sendMessage(prefix + "FTPClient was already connected. Disconnecting");
|
||||
ftpClient.logout();
|
||||
ftpClient.disconnect();
|
||||
ftpClient = new FTPClient();
|
||||
}
|
||||
sendFTP(sender, zipFile, ftpClient, zipFileStream, removeFilePath);
|
||||
if (deleteZipOnFTP)
|
||||
zipFile.delete();
|
||||
} catch (Exception | Error e) {
|
||||
sender.sendMessage(
|
||||
prefix + " FAILED TO FTP TRANSFER FILE: " + zipFile.getName() + ". ERROR IN CONSOLE.");
|
||||
if (deleteZipOnFail)
|
||||
zipFile.delete();
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (ftpClient.isConnected()) {
|
||||
sender.sendMessage(prefix + "Disconnecting");
|
||||
ftpClient.logout();
|
||||
ftpClient.disconnect();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}.runTaskAsynchronously(this);
|
||||
}
|
||||
|
||||
public void sendFTP(CommandSender sender, File zipFile, FTPClient ftpClient, FileInputStream zipFileStream, String path)
|
||||
throws SocketException, IOException {
|
||||
ftpClient.connect(serverFTP, portFTP);
|
||||
ftpClient.login(userFTP, passwordFTP);
|
||||
ftpClient.enterLocalPassiveMode();
|
||||
|
||||
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
|
||||
|
||||
boolean done = ftpClient.storeFile(path + zipFile.getName(), zipFileStream);
|
||||
zipFileStream.close();
|
||||
if (done) {
|
||||
sender.sendMessage(prefix + " Transfered backup using FTP!");
|
||||
} else {
|
||||
sender.sendMessage(prefix + " Something failed (maybe)! Status=" + ftpClient.getStatus());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public long toTime(String time) {
|
||||
long militime = 0;
|
||||
for(String split : time.split(",")) {
|
||||
split = split.trim();
|
||||
long k = 1;
|
||||
if (split.toUpperCase().endsWith("H")) {
|
||||
k *= 60 * 60;
|
||||
} else if (split.toUpperCase().endsWith("D")) {
|
||||
k *= 60 * 60 * 24;
|
||||
} else {
|
||||
k *= 60 * 60 * 24;
|
||||
}
|
||||
double j = Double.parseDouble(split.substring(0, split.length() - 1));
|
||||
militime += (j*k);
|
||||
}
|
||||
militime *= 1000;
|
||||
return militime;
|
||||
}
|
||||
|
||||
public void restore(File backup) {
|
||||
|
||||
//Kick all players
|
||||
for (Player player : Bukkit.getOnlinePlayers())
|
||||
player.kickPlayer(kickmessage);
|
||||
|
||||
//Disable all plugins safely.
|
||||
for (Plugin p : Bukkit.getPluginManager().getPlugins()) {
|
||||
if (p != this) {
|
||||
try {
|
||||
Bukkit.getPluginManager().disablePlugin(p);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
//Unload all worlds.
|
||||
List<String> names = new ArrayList<>();
|
||||
for (World w : Bukkit.getWorlds()) {
|
||||
for (Chunk c : w.getLoadedChunks()) {
|
||||
c.unload(false);
|
||||
}
|
||||
names.add(w.getName());
|
||||
Bukkit.unloadWorld(w, true);
|
||||
}
|
||||
for(String worldnames : names){
|
||||
File worldFile = new File(getMasterFolder(),worldnames);
|
||||
if(worldFile.exists())
|
||||
worldFile.delete();
|
||||
}
|
||||
|
||||
//Start overriding files.
|
||||
File parentTo = getMasterFolder().getParentFile();
|
||||
try {
|
||||
byte[] buffer = new byte[1024];
|
||||
ZipInputStream zis = new ZipInputStream(new FileInputStream(backup));
|
||||
ZipEntry zipEntry = zis.getNextEntry();
|
||||
while (zipEntry != null) {
|
||||
try {
|
||||
File newFile = newFile(parentTo, zipEntry);
|
||||
FileOutputStream fos = new FileOutputStream(newFile);
|
||||
int len;
|
||||
while ((len = zis.read(buffer)) > 0) {
|
||||
fos.write(buffer, 0, len);
|
||||
}
|
||||
fos.close();
|
||||
zipEntry = zis.getNextEntry();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
zis.closeEntry();
|
||||
zis.close();
|
||||
} catch (Exception e4) {
|
||||
e4.printStackTrace();
|
||||
}
|
||||
Bukkit.shutdown();
|
||||
}
|
||||
|
||||
public void zipFolder(String srcFolder, String destZipFile) throws Exception {
|
||||
ZipOutputStream zip = null;
|
||||
FileOutputStream fileWriter = null;
|
||||
|
||||
fileWriter = new FileOutputStream(destZipFile);
|
||||
zip = new ZipOutputStream(fileWriter);
|
||||
|
||||
|
||||
zip.setLevel(compression);
|
||||
|
||||
addFolderToZip("", srcFolder, zip);
|
||||
zip.flush();
|
||||
zip.close();
|
||||
}
|
||||
|
||||
private void addFileToZip(String path, String srcFile, ZipOutputStream zip) {
|
||||
try {
|
||||
File folder = new File(srcFile);
|
||||
if (!isExempt(srcFile)) {
|
||||
|
||||
if(!currentlySaving)
|
||||
return;
|
||||
// this.savedBytes += folder.length();
|
||||
if (folder.isDirectory()) {
|
||||
addFolderToZip(path, srcFile, zip);
|
||||
} else {
|
||||
if (folder.getName().endsWith("jar")) {
|
||||
if (path.contains("plugins") && (!savePluiginJars) || (!path.contains("plugins") && (!saveServerJar))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] buf = new byte['?'];
|
||||
|
||||
FileInputStream in = new FileInputStream(srcFile);
|
||||
zip.putNextEntry(new ZipEntry(path + separator + folder.getName()));
|
||||
int len;
|
||||
while ((len = in.read(buf)) > 0) {
|
||||
zip.write(buf, 0, len);
|
||||
}
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
}catch (FileNotFoundException e4){
|
||||
Bukkit.getConsoleSender().sendMessage(prefix + " FAILED TO ZIP FILE: " + srcFile+" Reason: "+e4.getClass().getName());
|
||||
e4.printStackTrace();
|
||||
}catch (IOException e5){
|
||||
if(!srcFile.endsWith(".db")) {
|
||||
Bukkit.getConsoleSender().sendMessage(prefix + " FAILED TO ZIP FILE: " + srcFile + " Reason: " + e5.getClass().getName());
|
||||
e5.printStackTrace();
|
||||
}else{
|
||||
Bukkit.getConsoleSender().sendMessage(prefix + " Skipping file " + srcFile +" due to another process that has locked a portion of the file");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void addFolderToZip(String path, String srcFolder, ZipOutputStream zip) {
|
||||
if ((!path.toLowerCase().contains("backups")) && (!isExempt(path))) {
|
||||
try {
|
||||
File folder = new File(srcFolder);
|
||||
String[] arrayOfString;
|
||||
int j = (arrayOfString = folder.list()).length;
|
||||
for (int i = 0; i < j; i++) {
|
||||
if(!currentlySaving)
|
||||
break;
|
||||
String fileName = arrayOfString[i];
|
||||
if (path.equals("")) {
|
||||
addFileToZip(folder.getName(), srcFolder + separator + fileName, zip);
|
||||
} else {
|
||||
addFileToZip(path + separator + folder.getName(), srcFolder + separator + fileName, zip);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long toByteSize(String s) {
|
||||
long k = Long.parseLong(s.substring(0, s.length() - 1));
|
||||
if (s.toUpperCase().endsWith("G")) {
|
||||
k *= 1000 * 1000 * 1000;
|
||||
} else if (s.toUpperCase().endsWith("M")) {
|
||||
k *= 1000 * 1000;
|
||||
} else if (s.toUpperCase().endsWith("K")) {
|
||||
k *= 1000;
|
||||
} else {
|
||||
k *= 10;
|
||||
}
|
||||
return k;
|
||||
}
|
||||
}
|
756
src/main/java/zombie_striker/sr/Updater.java
Normal file
756
src/main/java/zombie_striker/sr/Updater.java
Normal file
@@ -0,0 +1,756 @@
|
||||
package me.zombie_striker.sr;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipException;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* Checks and auto updates a plugin<br>
|
||||
* <br>
|
||||
* <b>You must have a option in your config to disable the updater!</b>
|
||||
*
|
||||
* @author Arsen
|
||||
*/
|
||||
public class Updater {
|
||||
|
||||
private static final String HOST = "https://api.curseforge.com";
|
||||
private static final String QUERY = "/servermods/files?projectIds=";
|
||||
private static final String AGENT = "Mozilla/5.0 Updater by ArsenArsen";
|
||||
private static final File WORKING_DIR = new File("plugins" + File.separator + "AUpdater" + File.separator);
|
||||
private static final File BACKUP_DIR = new File(WORKING_DIR, "backups" + File.separator);
|
||||
private static final File LOG_FILE = new File(WORKING_DIR, "updater.log");
|
||||
private static final File CONFIG_FILE = new File(WORKING_DIR, "global.yml");
|
||||
private static final char[] HEX_CHAR_ARRAY = "0123456789abcdef".toCharArray();
|
||||
private static final Pattern NAME_MATCH = Pattern.compile(".+\\sv?[0-9.]+");
|
||||
private static final String VERSION_SPLIT = "\\sv?";
|
||||
|
||||
|
||||
private int id = -1;
|
||||
|
||||
private Plugin p;
|
||||
private boolean debug = false;
|
||||
private UpdateAvailability lastCheck = null;
|
||||
private UpdateResult lastUpdate = UpdateResult.NOT_UPDATED;
|
||||
private File pluginFile = null;
|
||||
private String downloadURL = null;
|
||||
private String futuremd5;
|
||||
private String downloadName;
|
||||
private List<Channel> allowedChannels = Arrays.asList(Channel.ALPHA, Channel.BETA, Channel.RELEASE);
|
||||
private List<UpdateCallback> callbacks = new ArrayList<>();
|
||||
private SyncCallbackCaller caller = new SyncCallbackCaller();
|
||||
private List<String> skipTags = new ArrayList<>();
|
||||
private String latest;
|
||||
private FileConfiguration global;
|
||||
|
||||
public boolean updaterActive = false;
|
||||
|
||||
/**
|
||||
* Makes the updater for a plugin
|
||||
*
|
||||
* @param p Plugin to update
|
||||
*/
|
||||
public Updater(Plugin p) {
|
||||
this.p = p;
|
||||
try {
|
||||
pluginFile = new File(URLDecoder.decode(p.getClass().getProtectionDomain().getCodeSource().getLocation().getPath(), "UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
debug(e.toString());
|
||||
// Should not ever happen
|
||||
}
|
||||
latest = p.getDescription().getVersion();
|
||||
if (!CONFIG_FILE.exists()) {
|
||||
try {
|
||||
CONFIG_FILE.getParentFile().mkdirs();
|
||||
CONFIG_FILE.createNewFile();
|
||||
log("Created config file!");
|
||||
} catch (IOException e) {
|
||||
p.getLogger().log(Level.SEVERE, "Could not create " + CONFIG_FILE.getName() + "!", e);
|
||||
}
|
||||
}
|
||||
global = YamlConfiguration.loadConfiguration(CONFIG_FILE);
|
||||
global.options().header("Updater by ArsenArsen\nGlobal config\nSets should updates be downloaded globaly");
|
||||
if (!global.isSet("update")) {
|
||||
global.set("update", true);
|
||||
try {
|
||||
global.save(CONFIG_FILE);
|
||||
} catch (IOException e) {
|
||||
p.getLogger().log(Level.SEVERE, "Could not save default config file!", e);
|
||||
}
|
||||
}
|
||||
if (!LOG_FILE.exists()) {
|
||||
try {
|
||||
LOG_FILE.getParentFile().mkdirs();
|
||||
LOG_FILE.createNewFile();
|
||||
log("Created log file!");
|
||||
} catch (IOException e) {
|
||||
p.getLogger().log(Level.SEVERE, "Could not create " + LOG_FILE.getName() + "!", e);
|
||||
}
|
||||
}
|
||||
updaterActive = global.getBoolean("update");
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the updater for a plugin with an ID
|
||||
*
|
||||
* @param p The plugin
|
||||
* @param id Plugin ID
|
||||
*/
|
||||
public Updater(Plugin p, int id) {
|
||||
this(p);
|
||||
setID(id);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the updater for a plugin with an ID
|
||||
*
|
||||
* @param p The plugin
|
||||
* @param id Plugin ID
|
||||
* @param download Set to true if your plugin needs to be immediately downloaded
|
||||
* @param skipTags Tags, endings of a filename, that updater will ignore, must begin with a dash ('-')
|
||||
*/
|
||||
public Updater(Plugin p, int id, boolean download, String... skipTags) {
|
||||
this(p);
|
||||
setID(id);
|
||||
for (String tag : skipTags)
|
||||
if (tag.startsWith("-"))
|
||||
this.skipTags.add(tag);
|
||||
if (download && (checkForUpdates() == UpdateAvailability.UPDATE_AVAILABLE)) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the updater for a plugin with an ID
|
||||
*
|
||||
* @param p The plugin
|
||||
* @param id Plugin ID
|
||||
* @param download Set to true if your plugin needs to be immediately downloaded
|
||||
* @param skipTags Tags, endings of a filename, that updater will ignore, or null for none
|
||||
* @param callbacks All update callbacks you need
|
||||
*/
|
||||
public Updater(Plugin p, int id, boolean download, String[] skipTags, UpdateCallback... callbacks) {
|
||||
this(p);
|
||||
setID(id);
|
||||
this.callbacks.addAll(Arrays.asList(callbacks));
|
||||
if (skipTags != null) {
|
||||
for (String tag : skipTags)
|
||||
if (tag.startsWith("-"))
|
||||
this.skipTags.add(tag);
|
||||
}
|
||||
if (global.getBoolean("update", true) && download && (checkForUpdates() == UpdateAvailability.UPDATE_AVAILABLE)) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin ID
|
||||
*
|
||||
* @return the plugin ID
|
||||
*/
|
||||
public int getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the plugin ID
|
||||
*
|
||||
* @param id The plugin ID
|
||||
*/
|
||||
public void setID(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new callback
|
||||
*
|
||||
* @param callback Callback to register
|
||||
*/
|
||||
public void registerCallback(UpdateCallback callback) {
|
||||
callbacks.add(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts a update
|
||||
*
|
||||
* @throws IllegalStateException if the ID was not set
|
||||
*/
|
||||
public void update() {
|
||||
debug(WORKING_DIR.getAbsolutePath());
|
||||
debug("Update!");
|
||||
if (id == -1) {
|
||||
throw new IllegalStateException("Plugin ID is not set!");
|
||||
}
|
||||
|
||||
if (lastCheck == null) {
|
||||
checkForUpdates();
|
||||
}
|
||||
|
||||
if (!BACKUP_DIR.exists() || !BACKUP_DIR.isDirectory()) {
|
||||
BACKUP_DIR.mkdir();
|
||||
}
|
||||
final Updater updater = this;
|
||||
if (!global.getBoolean("update", true)) {
|
||||
lastUpdate = UpdateResult.DISABLED;
|
||||
debug("Disabled!");
|
||||
caller.call(callbacks, UpdateResult.DISABLED, updater);
|
||||
return;
|
||||
}
|
||||
if (lastCheck == UpdateAvailability.UPDATE_AVAILABLE) {
|
||||
new BukkitRunnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
debug("Update STARTED!");
|
||||
p.getLogger().info("Starting update of " + p.getName());
|
||||
log("Updating " + p.getName() + "!");
|
||||
lastUpdate = download(true);
|
||||
p.getLogger().log(Level.INFO, "Update done! Result: " + lastUpdate);
|
||||
caller.call(callbacks, lastUpdate, updater);
|
||||
}
|
||||
}.runTaskAsynchronously(p);
|
||||
} else if (lastCheck == UpdateAvailability.SM_UNREACHABLE) {
|
||||
lastUpdate = UpdateResult.IOERROR;
|
||||
debug("Fail!");
|
||||
caller.call(callbacks, UpdateResult.IOERROR, updater);
|
||||
} else {
|
||||
lastUpdate = UpdateResult.GENERAL_ERROR;
|
||||
debug("Fail!");
|
||||
caller.call(callbacks, UpdateResult.IOERROR, updater);
|
||||
}
|
||||
}
|
||||
|
||||
public UpdateResult download(boolean keepBackups) {
|
||||
try {
|
||||
if(keepBackups){
|
||||
Files.copy(pluginFile.toPath(),
|
||||
new File(BACKUP_DIR, "backup-" + System.currentTimeMillis() + "-" + p.getName() + ".jar").toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING);
|
||||
//TODO: Considering the amount of times the plugin updates, I don't want there to be a huge file full of old jars.
|
||||
}
|
||||
File downloadTo = new File(pluginFile.getParentFile().getAbsolutePath() +
|
||||
File.separator + "AUpdater" + File.separator, downloadName);
|
||||
downloadTo.getParentFile().mkdirs();
|
||||
downloadTo.delete();
|
||||
if(keepBackups)
|
||||
debug("Started download!");
|
||||
|
||||
downloadIsSeperateBecauseGotoGotRemoved(downloadTo);
|
||||
|
||||
if(keepBackups){
|
||||
debug("Ended download!");
|
||||
if (!fileHash(downloadTo).equalsIgnoreCase(futuremd5))
|
||||
return UpdateResult.BAD_HASH;
|
||||
if (downloadTo.getName().endsWith(".jar")) {
|
||||
pluginFile.setWritable(true, false);
|
||||
pluginFile.delete();
|
||||
if(keepBackups){
|
||||
debug("Started copy!");
|
||||
InputStream in = new FileInputStream(downloadTo);
|
||||
File file = new File(pluginFile.getParentFile()
|
||||
.getAbsoluteFile() + File.separator + "update" + File.separator, pluginFile.getName());
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
long bytes = copy(in, out);
|
||||
p.getLogger().info("Update done! Downloaded " + bytes + " bytes!");
|
||||
log("Updated plugin " + p.getName() + " with " + bytes + "bytes!");
|
||||
}
|
||||
return UpdateResult.UPDATE_SUCCEEDED;
|
||||
} else
|
||||
return unzip(downloadTo);
|
||||
|
||||
|
||||
}else{
|
||||
return UpdateResult.UPDATE_SUCCEEDED;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
p.getLogger().log(Level.SEVERE, "Couldn't download update for " + p.getName(), e);
|
||||
log("Failed to update " + p.getName() + "!", e);
|
||||
return UpdateResult.IOERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* God damn it Gosling, <a href="http://stackoverflow.com/a/4547764/3809164">reference here.</a>
|
||||
*/
|
||||
private void downloadIsSeperateBecauseGotoGotRemoved(File downloadTo) throws IOException {
|
||||
URL url = new URL(downloadURL);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.addRequestProperty("User-Agent", AGENT);
|
||||
connection.connect();
|
||||
if (connection.getResponseCode() >= 300 && connection.getResponseCode() < 400) {
|
||||
downloadURL = connection.getHeaderField("Location");
|
||||
downloadIsSeperateBecauseGotoGotRemoved(downloadTo);
|
||||
} else {
|
||||
debug(connection.getResponseCode() + " " + connection.getResponseMessage() + " when requesting " + downloadURL);
|
||||
copy(connection.getInputStream(), new FileOutputStream(downloadTo));
|
||||
}
|
||||
}
|
||||
|
||||
private long copy(InputStream in, OutputStream out) throws IOException {
|
||||
long bytes = 0;
|
||||
byte[] buf = new byte[0x1000];
|
||||
while (true) {
|
||||
int r = in.read(buf);
|
||||
if (r == -1)
|
||||
break;
|
||||
out.write(buf, 0, r);
|
||||
bytes += r;
|
||||
debug("Another 4K, current: " + r);
|
||||
}
|
||||
out.flush();
|
||||
out.close();
|
||||
in.close();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
private UpdateResult unzip(File download) {
|
||||
ZipFile zipFile = null;
|
||||
try {
|
||||
zipFile = new ZipFile(download);
|
||||
Enumeration<? extends ZipEntry> entries = zipFile.entries();
|
||||
ZipEntry entry;
|
||||
File updateFile = new File(pluginFile.getParentFile()
|
||||
.getAbsoluteFile() + File.separator + "update" + File.separator, pluginFile.getName());
|
||||
while ((entry = entries.nextElement()) != null) {
|
||||
File target = new File(updateFile, entry.getName());
|
||||
File inPlugins = new File(pluginFile.getParentFile(), entry.getName());
|
||||
if(!inPlugins.exists()){
|
||||
target = inPlugins;
|
||||
}
|
||||
if (!entry.isDirectory()) {
|
||||
target.getParentFile().mkdirs();
|
||||
InputStream zipStream = zipFile.getInputStream(entry);
|
||||
OutputStream fileStream = new FileOutputStream(target);
|
||||
copy(zipStream, fileStream);
|
||||
}
|
||||
}
|
||||
return UpdateResult.UPDATE_SUCCEEDED;
|
||||
} catch (IOException e) {
|
||||
if (e instanceof ZipException) {
|
||||
p.getLogger().log(Level.SEVERE, "Could not unzip downloaded file!", e);
|
||||
log("Update for " + p.getName() + "was an unknown filetype! ", e);
|
||||
return UpdateResult.UNKNOWN_FILE_TYPE;
|
||||
} else {
|
||||
p.getLogger().log(Level.SEVERE,
|
||||
"An IOException occured while trying to update %s!".replace("%s", p.getName()), e);
|
||||
log("Update for " + p.getName() + "was an unknown filetype! ", e);
|
||||
return UpdateResult.IOERROR;
|
||||
}
|
||||
} finally {
|
||||
if (zipFile != null) {
|
||||
try {
|
||||
zipFile.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for new updates
|
||||
*
|
||||
* @param force Discards the cached state in order to get a new one, ignored if update check didn't run
|
||||
* @return Is there any updates
|
||||
* @throws IllegalStateException If the plugin ID is not set
|
||||
*/
|
||||
public UpdateAvailability checkForUpdates(boolean force) {
|
||||
if (id == -1) {
|
||||
throw new IllegalStateException("Plugin ID is not set!");
|
||||
}
|
||||
|
||||
if (force || lastCheck == null) {
|
||||
String target = HOST + QUERY + id;
|
||||
debug(target);
|
||||
try {
|
||||
URL url = new URL(target);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.addRequestProperty("User-Agent", AGENT);
|
||||
connection.connect();
|
||||
debug("Connecting!");
|
||||
BufferedReader responseReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
StringBuilder responseBuffer = new StringBuilder();
|
||||
String line;
|
||||
while ((line = responseReader.readLine()) != null) {
|
||||
responseBuffer.append(line);
|
||||
}
|
||||
debug("All read!");
|
||||
responseReader.close();
|
||||
String response = responseBuffer.toString();
|
||||
int counter = 1;
|
||||
if (connection.getResponseCode() == 200) {
|
||||
|
||||
try {
|
||||
debug("RESCODE 200");
|
||||
while (true) {
|
||||
debug("Counter: " + counter);
|
||||
JSONParser parser = new JSONParser();
|
||||
JSONArray json = (JSONArray) parser.parse(response);
|
||||
if (json.size() - counter < 0) {
|
||||
lastCheck = UpdateAvailability.NO_UPDATE;
|
||||
debug("No update!");
|
||||
break;
|
||||
}
|
||||
JSONObject latest = (JSONObject) json.get(json.size() - counter);
|
||||
futuremd5 = (String) latest.get("md5");
|
||||
String channel = (String) latest.get("releaseType");
|
||||
String name = (String) latest.get("name");
|
||||
if (allowedChannels.contains(Channel.matchChannel(channel.toUpperCase()))
|
||||
&& !hasTag(name)) {
|
||||
String noTagName = name;
|
||||
String oldVersion = p.getDescription().getVersion().replaceAll("-.*", "");
|
||||
for (String tag : skipTags) {
|
||||
noTagName = noTagName.replace(tag, "");
|
||||
oldVersion = oldVersion.replace(tag, "");
|
||||
}
|
||||
if (!NAME_MATCH.matcher(noTagName).matches()) {
|
||||
lastCheck = UpdateAvailability.CANT_PARSE_NAME;
|
||||
return lastCheck;
|
||||
}
|
||||
String[] splitName = noTagName.split(VERSION_SPLIT);
|
||||
String version = splitName[splitName.length - 1];
|
||||
if (oldVersion.length() > version.length()) {
|
||||
while (oldVersion.length() > version.length()) {
|
||||
version += ".0";
|
||||
}
|
||||
} else if (oldVersion.length() < version.length()) {
|
||||
while (oldVersion.length() < version.length()) {
|
||||
oldVersion += ".0";
|
||||
}
|
||||
}
|
||||
debug("Versions are same length");
|
||||
String[] splitOldVersion = oldVersion.split("\\.");
|
||||
String[] splitVersion = version.split("\\.");
|
||||
|
||||
Integer[] parsedOldVersion = new Integer[splitOldVersion.length];
|
||||
Integer[] parsedVersion = new Integer[splitVersion.length];
|
||||
|
||||
for (int i = 0; i < parsedOldVersion.length; i++) {
|
||||
parsedOldVersion[i] = Integer.parseInt(splitOldVersion[i]);
|
||||
}
|
||||
for (int i = 0; i < parsedVersion.length; i++) {
|
||||
parsedVersion[i] = Integer.parseInt(splitVersion[i]);
|
||||
}
|
||||
boolean update = false;
|
||||
for (int i = 0; i < parsedOldVersion.length; i++) {
|
||||
if (parsedOldVersion[i] < parsedVersion[i]) {
|
||||
update = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!update) {
|
||||
lastCheck = UpdateAvailability.NO_UPDATE;
|
||||
//Temp fix for downloads
|
||||
downloadURL = ((String) latest.get("downloadUrl")).replace(" ", "%20");
|
||||
downloadName = (String) latest.get("fileName");
|
||||
} else {
|
||||
lastCheck = UpdateAvailability.UPDATE_AVAILABLE;
|
||||
downloadURL = ((String) latest.get("downloadUrl")).replace(" ", "%20");
|
||||
downloadName = (String) latest.get("fileName");
|
||||
}
|
||||
break;
|
||||
} else
|
||||
counter++;
|
||||
}
|
||||
debug("While loop over!");
|
||||
} catch (ParseException e) {
|
||||
p.getLogger().log(Level.SEVERE, "Could not parse API Response for " + target, e);
|
||||
log("Could not parse API Response for " + target + " while updating " + p.getName(), e);
|
||||
lastCheck = UpdateAvailability.CANT_UNDERSTAND;
|
||||
}
|
||||
} else {
|
||||
log("Could not reach API for " + target + " while updating " + p.getName());
|
||||
lastCheck = UpdateAvailability.SM_UNREACHABLE;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
p.getLogger().log(Level.SEVERE, "Could not check for updates for plugin " + p.getName(), e);
|
||||
log("Could not reach API for " + target + " while updating " + p.getName(), e);
|
||||
lastCheck = UpdateAvailability.SM_UNREACHABLE;
|
||||
}
|
||||
}
|
||||
log("Update check ran for " + p.getName() + "! Check resulted in " + lastCheck);
|
||||
return lastCheck;
|
||||
}
|
||||
|
||||
private void debug(String message) {
|
||||
if (debug)
|
||||
p.getLogger().info(message + ' ' + new Throwable().getStackTrace()[1]);
|
||||
}
|
||||
|
||||
private void log(String message) {
|
||||
try {
|
||||
Files.write(LOG_FILE.toPath(), Collections.singletonList(
|
||||
"[" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "] " + message),
|
||||
StandardCharsets.UTF_8, StandardOpenOption.APPEND);
|
||||
} catch (IOException e) {
|
||||
p.getLogger().log(Level.SEVERE, "Could not log to " + LOG_FILE.getAbsolutePath() + "!", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void log(String message, Exception exception) {
|
||||
StringWriter string = new StringWriter();
|
||||
PrintWriter print = new PrintWriter(string);
|
||||
exception.printStackTrace(print);
|
||||
log(message + " " + string.toString());
|
||||
try {
|
||||
string.close();
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
print.close();
|
||||
}
|
||||
|
||||
private boolean hasTag(String name) {
|
||||
for (String tag : skipTags) {
|
||||
if (name.toLowerCase().endsWith(tag.toLowerCase())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for new updates, non forcing cache override
|
||||
*
|
||||
* @return Is there any updates
|
||||
* @throws IllegalStateException If the plugin ID is not set
|
||||
*/
|
||||
public UpdateAvailability checkForUpdates() {
|
||||
return checkForUpdates(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks did the update run successfully
|
||||
*
|
||||
* @return The update state
|
||||
*/
|
||||
public UpdateResult isUpdated() {
|
||||
return lastUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets allowed channels, AKA release types
|
||||
*
|
||||
* @param channels The allowed channels
|
||||
*/
|
||||
public void setChannels(Channel... channels) {
|
||||
allowedChannels.clear();
|
||||
allowedChannels.addAll(Arrays.asList(channels));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the latest version
|
||||
*
|
||||
* @return The latest version
|
||||
*/
|
||||
public String getLatest() {
|
||||
return latest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the outcome of an update
|
||||
*
|
||||
* @author Arsen
|
||||
*/
|
||||
public enum UpdateResult {
|
||||
/**
|
||||
* Update was successful
|
||||
*/
|
||||
UPDATE_SUCCEEDED,
|
||||
|
||||
/**
|
||||
* Update was not attempted yet
|
||||
*/
|
||||
NOT_UPDATED,
|
||||
|
||||
/**
|
||||
* Could not unpack the update
|
||||
*/
|
||||
UNKNOWN_FILE_TYPE,
|
||||
|
||||
/**
|
||||
* Miscellanies error occurred while update checking
|
||||
*/
|
||||
GENERAL_ERROR,
|
||||
|
||||
/**
|
||||
* Updater is globally disabled
|
||||
*/
|
||||
DISABLED,
|
||||
/**
|
||||
* The hashing algorithm and the remote hash had different results.
|
||||
*/
|
||||
BAD_HASH,
|
||||
/**
|
||||
* An unknown IO error occurred
|
||||
*/
|
||||
IOERROR
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the outcome of an update check
|
||||
*
|
||||
* @author Arsen
|
||||
*/
|
||||
public enum UpdateAvailability {
|
||||
/**
|
||||
* There is an update
|
||||
*/
|
||||
UPDATE_AVAILABLE,
|
||||
|
||||
/**
|
||||
* You have the latest version
|
||||
*/
|
||||
NO_UPDATE,
|
||||
|
||||
/**
|
||||
* Could not reach server mods API
|
||||
*/
|
||||
SM_UNREACHABLE,
|
||||
|
||||
/**
|
||||
* Update name cannot be parsed, meaning the version cannot be compared
|
||||
*/
|
||||
CANT_PARSE_NAME,
|
||||
|
||||
/**
|
||||
* Could not parse response from server mods API
|
||||
*/
|
||||
CANT_UNDERSTAND
|
||||
}
|
||||
|
||||
public enum Channel {
|
||||
/**
|
||||
* Normal release
|
||||
*/
|
||||
RELEASE("release"),
|
||||
|
||||
/**
|
||||
* Beta release
|
||||
*/
|
||||
BETA("beta"),
|
||||
|
||||
/**
|
||||
* Alpha release
|
||||
*/
|
||||
ALPHA("alpha");
|
||||
|
||||
private String channel;
|
||||
|
||||
Channel(String channel) {
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the channel value
|
||||
*
|
||||
* @return the channel value
|
||||
*/
|
||||
public String getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns channel whose channel value matches the given string
|
||||
*
|
||||
* @param channel The channel value
|
||||
* @return The Channel constant
|
||||
*/
|
||||
public static Channel matchChannel(String channel) {
|
||||
for (Channel c : values()) {
|
||||
if (c.channel.equalsIgnoreCase(channel)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates files MD5 hash
|
||||
*
|
||||
* @param file The file to digest
|
||||
* @return The MD5 hex or null, if the operation failed
|
||||
*/
|
||||
public String fileHash(File file) {
|
||||
FileInputStream is;
|
||||
try {
|
||||
is = new FileInputStream(file);
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] bytes = new byte[2048];
|
||||
int numBytes;
|
||||
while ((numBytes = is.read(bytes)) != -1) {
|
||||
md.update(bytes, 0, numBytes);
|
||||
}
|
||||
byte[] digest = md.digest();
|
||||
char[] hexChars = new char[digest.length * 2];
|
||||
for (int j = 0; j < digest.length; j++) {
|
||||
int v = digest[j] & 0xFF;
|
||||
hexChars[j * 2] = HEX_CHAR_ARRAY[v >>> 4];
|
||||
hexChars[j * 2 + 1] = HEX_CHAR_ARRAY[v & 0x0F];
|
||||
}
|
||||
is.close();
|
||||
return new String(hexChars);
|
||||
} catch (IOException | NoSuchAlgorithmException e) {
|
||||
p.getLogger().log(Level.SEVERE, "Could not digest " + file.getPath(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called right after update is done
|
||||
*
|
||||
* @author Arsen
|
||||
*/
|
||||
public interface UpdateCallback {
|
||||
|
||||
void updated(UpdateResult updateResult, Updater updater);
|
||||
}
|
||||
|
||||
private class SyncCallbackCaller extends BukkitRunnable {
|
||||
private List<UpdateCallback> callbacks;
|
||||
private UpdateResult updateResult;
|
||||
private Updater updater;
|
||||
|
||||
public void run() {
|
||||
for (UpdateCallback callback : callbacks) {
|
||||
callback.updated(updateResult, updater);
|
||||
}
|
||||
}
|
||||
|
||||
void call(List<UpdateCallback> callbacks, UpdateResult updateResult, Updater updater) {
|
||||
this.callbacks = callbacks;
|
||||
this.updateResult = updateResult;
|
||||
this.updater = updater;
|
||||
if (!Bukkit.getServer().isPrimaryThread())
|
||||
runTask(updater.p);
|
||||
else run();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user