/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.royaldev.royalcommands.rcommands;
import com.google.common.io.Files;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.royaldev.royalcommands.MessageColor;
import org.royaldev.royalcommands.RUtils;
import org.royaldev.royalcommands.RoyalCommands;
import org.royaldev.royalcommands.UnZip;
import org.royaldev.royalcommands.rcommands.pluginmanager.SCmdCommands;
import org.royaldev.royalcommands.rcommands.pluginmanager.SCmdDelete;
import org.royaldev.royalcommands.rcommands.pluginmanager.SCmdDisable;
import org.royaldev.royalcommands.rcommands.pluginmanager.SCmdDownload;
import org.royaldev.royalcommands.rcommands.pluginmanager.SCmdDownloadLink;
import org.royaldev.royalcommands.rcommands.pluginmanager.SCmdEnable;
import org.royaldev.royalcommands.rcommands.pluginmanager.SCmdFindTag;
import org.royaldev.royalcommands.rcommands.pluginmanager.SCmdInfo;
import org.royaldev.royalcommands.rcommands.pluginmanager.SCmdList;
import org.royaldev.royalcommands.rcommands.pluginmanager.SCmdLoad;
import org.royaldev.royalcommands.rcommands.pluginmanager.SCmdReload;
import org.royaldev.royalcommands.rcommands.pluginmanager.SCmdReloadAll;
import org.royaldev.royalcommands.rcommands.pluginmanager.SCmdUnload;
import org.royaldev.royalcommands.rcommands.pluginmanager.SCmdUpdate;
import org.royaldev.royalcommands.rcommands.pluginmanager.SCmdUpdateCheck;
import org.royaldev.royalcommands.rcommands.pluginmanager.SCmdUpdateCheckAll;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
@ReflectCommand
public class CmdPluginManager extends ParentCommand {
public CmdPluginManager(final RoyalCommands instance, final String name) {
super(instance, name, true);
this.addSubCommand(new SCmdCommands(this.plugin, this));
this.addSubCommand(new SCmdDelete(this.plugin, this));
this.addSubCommand(new SCmdDisable(this.plugin, this));
this.addSubCommand(new SCmdDownload(this.plugin, this));
this.addSubCommand(new SCmdDownloadLink(this.plugin, this));
this.addSubCommand(new SCmdEnable(this.plugin, this));
this.addSubCommand(new SCmdFindTag(this.plugin, this));
this.addSubCommand(new SCmdInfo(this.plugin, this));
this.addSubCommand(new SCmdList(this.plugin, this));
this.addSubCommand(new SCmdLoad(this.plugin, this));
this.addSubCommand(new SCmdReload(this.plugin, this));
this.addSubCommand(new SCmdReloadAll(this.plugin, this));
this.addSubCommand(new SCmdUnload(this.plugin, this));
this.addSubCommand(new SCmdUpdate(this.plugin, this));
this.addSubCommand(new SCmdUpdateCheck(this.plugin, this));
this.addSubCommand(new SCmdUpdateCheckAll(this.plugin, this));
}
/**
* Gets the names of all plugins that depend on the specified plugin.
* <p/>
* This will not return plugins that are disabled.
*
* @param name Plugin name to find dependencies of
* @return List of dependencies, may be empty - never null
*/
private List<String> getDependedOnBy(String name) {
final List<String> dependedOnBy = new ArrayList<>();
for (Plugin pl : this.plugin.getServer().getPluginManager().getPlugins()) {
if (pl == null) continue;
if (!pl.isEnabled()) continue;
PluginDescriptionFile pdf = pl.getDescription();
if (pdf == null) continue;
List<String> depends = pdf.getDepend();
if (depends == null) continue;
for (String depend : depends) if (name.equalsIgnoreCase(depend)) dependedOnBy.add(pl.getName());
}
return dependedOnBy;
}
public boolean downloadAndMovePlugin(String url, String saveAs, boolean recursive, CommandSender cs) {
if (saveAs == null) saveAs = "";
BufferedInputStream bis;
final HttpURLConnection huc;
try {
huc = (HttpURLConnection) new URL(url).openConnection();
huc.setInstanceFollowRedirects(true);
huc.connect();
bis = new BufferedInputStream(huc.getInputStream());
} catch (MalformedURLException e) {
cs.sendMessage(MessageColor.NEGATIVE + "The download link is invalid!");
return false;
} catch (IOException e) {
cs.sendMessage(MessageColor.NEGATIVE + "An internal input/output error occurred. Please try again. (" + MessageColor.NEUTRAL + e.getMessage() + MessageColor.NEGATIVE + ")");
return false;
}
String[] urlParts = huc.getURL().toString().split("(\\\\|/)");
final String fileName = (!saveAs.isEmpty()) ? saveAs : urlParts[urlParts.length - 1];
cs.sendMessage(MessageColor.POSITIVE + "Creating temporary folder...");
File f = new File(System.getProperty("java.io.tmpdir") + File.separator + UUID.randomUUID().toString() + File.separator + fileName);
while (f.getParentFile().exists()) // make sure we get our own directory
f = new File(System.getProperty("java.io.tmpdir") + File.separator + UUID.randomUUID().toString() + File.separator + fileName);
if (!fileName.endsWith(".zip") && !fileName.endsWith(".jar")) {
cs.sendMessage(MessageColor.NEGATIVE + "The file wasn't a zip or jar file, so it was not downloaded.");
cs.sendMessage(MessageColor.NEGATIVE + "Filename: " + MessageColor.NEUTRAL + fileName);
return false;
}
//noinspection ResultOfMethodCallIgnored
f.getParentFile().mkdirs();
BufferedOutputStream bos;
try {
bos = new BufferedOutputStream(new FileOutputStream(f));
} catch (FileNotFoundException e) {
cs.sendMessage(MessageColor.NEGATIVE + "The temporary download folder was not found. Make sure that " + MessageColor.NEUTRAL + System.getProperty("java.io.tmpdir") + MessageColor.NEGATIVE + " is writable.");
return false;
}
int b;
cs.sendMessage(MessageColor.POSITIVE + "Downloading file " + MessageColor.NEUTRAL + fileName + MessageColor.POSITIVE + "...");
try {
try {
while ((b = bis.read()) != -1) bos.write(b);
} finally {
bos.flush();
bos.close();
}
} catch (IOException e) {
cs.sendMessage(MessageColor.NEGATIVE + "An internal input/output error occurred. Please try again. (" + MessageColor.NEUTRAL + e.getMessage() + MessageColor.NEGATIVE + ")");
return false;
}
if (fileName.endsWith(".zip")) {
cs.sendMessage(MessageColor.POSITIVE + "Decompressing zip...");
UnZip.decompress(f.getAbsolutePath(), f.getParent());
}
for (File fi : RUtils.listFiles(f.getParentFile(), recursive)) {
if (!fi.getName().endsWith(".jar")) continue;
cs.sendMessage(MessageColor.POSITIVE + "Moving " + MessageColor.NEUTRAL + fi.getName() + MessageColor.POSITIVE + " to plugins folder...");
try {
Files.move(fi, new File(this.plugin.getDataFolder().getParentFile() + File.separator + fi.getName()));
} catch (IOException e) {
cs.sendMessage(MessageColor.NEGATIVE + "Couldn't move " + MessageColor.NEUTRAL + fi.getName() + MessageColor.NEGATIVE + ": " + MessageColor.NEUTRAL + e.getMessage());
}
}
cs.sendMessage(MessageColor.POSITIVE + "Removing temporary folder...");
RUtils.deleteDirectory(f.getParentFile());
return true;
}
public String getCustomTag(String name) {
ConfigurationSection cs = this.plugin.getConfig().getConfigurationSection("pluginmanager.custom_tags");
if (cs == null) return null;
for (String key : cs.getKeys(false)) {
if (!key.equalsIgnoreCase(name)) continue;
return cs.getString(key);
}
return null;
}
/**
* Gets the names of all plugins that depend on the specified plugin.
* <p/>
* This will not return plugins that are disabled.
*
* @param dep Plugin to find dependencies of
* @return List of dependencies, may be empty - never null
*/
public List<String> getDependedOnBy(Plugin dep) {
return getDependedOnBy(dep.getName());
}
public List<String> getMatchingJarNames(String arg) {
final List<String> completions = new ArrayList<>();
for (final String name : this.plugin.getDataFolder().getParentFile().list()) {
final String lowerCase = name.toLowerCase();
if (!lowerCase.endsWith(".jar") || !lowerCase.startsWith(arg)) continue;
completions.add(lowerCase.equals(arg) ? 0 : completions.size(), name);
}
return completions;
}
public void removePluginFromList(Plugin p) {
try {
@SuppressWarnings("unchecked")
final List<Plugin> plugins = (List<Plugin>) RUtils.getPrivateField(this.plugin.getServer().getPluginManager(), "plugins");
plugins.remove(p);
} catch (Exception e) {
e.printStackTrace();
}
}
public void unregisterAllPluginCommands(String pluginName) {
try {
Object result = RUtils.getPrivateField(this.plugin.getServer().getPluginManager(), "commandMap");
SimpleCommandMap commandMap = (SimpleCommandMap) result;
Object map = RUtils.getPrivateField(commandMap, "knownCommands");
@SuppressWarnings("unchecked") HashMap<String, Command> knownCommands = (HashMap<String, Command>) map;
final List<Command> commands = new ArrayList<>(commandMap.getCommands());
for (Command c : commands) {
if (!(c instanceof PluginCommand)) continue;
final PluginCommand pc = (PluginCommand) c;
if (!pc.getPlugin().getName().equals(pluginName)) continue;
knownCommands.remove(pc.getName());
for (String alias : pc.getAliases()) {
if (knownCommands.containsKey(alias)) {
final Command ac = knownCommands.get(alias);
if (!(ac instanceof PluginCommand)) continue;
final PluginCommand apc = (PluginCommand) ac;
if (!apc.getPlugin().getName().equals(pluginName)) continue;
knownCommands.remove(alias);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String updateCheck(String name, String currentVersion) throws Exception {
String tag = this.getCustomTag(name);
if (tag == null) tag = name.toLowerCase();
String pluginUrlString = "http://dev.bukkit.org/bukkit-plugins/" + tag + "/files.rss";
URL url = new URL(pluginUrlString);
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(url.openConnection().getInputStream());
doc.getDocumentElement().normalize();
NodeList nodes = doc.getElementsByTagName("item");
Node firstNode = nodes.item(0);
if (firstNode.getNodeType() == 1) {
Element firstElement = (Element) firstNode;
NodeList firstElementTagName = firstElement.getElementsByTagName("title");
Element firstNameElement = (Element) firstElementTagName.item(0);
NodeList firstNodes = firstNameElement.getChildNodes();
return firstNodes.item(0).getNodeValue();
}
return currentVersion;
}
}