blob: 7b97fb5fb717a9e3903ac2ab6f0c7741d9bcb948 [file] [log] [blame] [raw]
package com.platymuus.bukkit.permissions;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
/**
* Main class for PermissionsBukkit.
*/
public class PermissionsPlugin extends JavaPlugin {
private PlayerListener playerListener = new PlayerListener(this);
private PermissionsCommand commandExecutor = new PermissionsCommand(this);
private PermissionsTabComplete tabCompleter = new PermissionsTabComplete(this);
private HashMap<String, PermissionAttachment> permissions = new HashMap<String, PermissionAttachment>();
private File configFile;
private YamlConfiguration config;
// -- Basic stuff
@Override
public void onEnable() {
// Take care of configuration
configFile = new File(getDataFolder(), "config.yml");
if (!configFile.exists()) {
saveDefaultConfig();
}
reloadConfig();
// Register stuff
getCommand("permissions").setExecutor(commandExecutor);
getCommand("permissions").setTabCompleter(tabCompleter);
getServer().getPluginManager().registerEvents(playerListener, this);
// Register everyone online right now
for (Player p : getServer().getOnlinePlayers()) {
registerPlayer(p);
}
// Metrics are fun!
try {
new PermissionsMetrics(this);
}
catch (IOException ex) {
getLogger().warning("Failed to connect to plugin metrics: " + ex.getMessage());
}
// How are you gentlemen
int count = getServer().getOnlinePlayers().length;
if (count > 0) {
getLogger().info("Enabled successfully, " + count + " online players registered");
} else {
// "0 players registered" sounds too much like an error
getLogger().info("Enabled successfully");
}
}
@Override
public FileConfiguration getConfig() {
return config;
}
@Override
public void reloadConfig() {
config = new YamlConfiguration();
config.options().pathSeparator('/');
try {
config.load(configFile);
} catch (Exception ex) {
getLogger().log(Level.SEVERE, "Failed to load configuration", ex);
}
}
@Override
public void saveConfig() {
// If there's no keys (such as in the event of a load failure) don't save
if (config.getKeys(true).size() > 0) {
try {
config.save(configFile);
} catch (IOException ex) {
getLogger().log(Level.SEVERE, "Failed to save configuration", ex);
}
}
}
@Override
public void onDisable() {
// Unregister everyone
for (Player p : getServer().getOnlinePlayers()) {
unregisterPlayer(p);
}
// Good day to you! I said good day!
int count = getServer().getOnlinePlayers().length;
if (count > 0) {
getLogger().info("Disabled successfully, " + count + " online players unregistered");
}
}
// -- External API
/**
* Get the group with the given name.
* @param groupName The name of the group.
* @return A Group if it exists or null otherwise.
*/
public Group getGroup(String groupName) {
if (getNode("groups") != null) {
for (String key : getNode("groups").getKeys(false)) {
if (key.equalsIgnoreCase(groupName)) {
return new Group(this, key);
}
}
}
return null;
}
/**
* Returns a list of groups a player is in.
* @param playerName The name of the player.
* @return The groups this player is in. May be empty.
*/
public List<Group> getGroups(String playerName) {
ArrayList<Group> result = new ArrayList<Group>();
if (getNode("users/" + playerName) != null) {
for (String key : getNode("users/" + playerName).getStringList("groups")) {
result.add(new Group(this, key));
}
} else {
result.add(new Group(this, "default"));
}
return result;
}
/**
* Returns permission info on the given player.
* @param playerName The name of the player.
* @return A PermissionsInfo about this player.
*/
public PermissionInfo getPlayerInfo(String playerName) {
if (getNode("users/" + playerName) == null) {
return null;
} else {
return new PermissionInfo(this, getNode("users/" + playerName), "groups");
}
}
/**
* Returns a list of all defined groups.
* @return The list of groups.
*/
public List<Group> getAllGroups() {
ArrayList<Group> result = new ArrayList<Group>();
if (getNode("groups") != null) {
for (String key : getNode("groups").getKeys(false)) {
result.add(new Group(this, key));
}
}
return result;
}
// -- Plugin stuff
protected void registerPlayer(Player player) {
if (permissions.containsKey(player.getName())) {
debug("Registering " + player.getName() + ": was already registered");
unregisterPlayer(player);
}
PermissionAttachment attachment = player.addAttachment(this);
permissions.put(player.getName(), attachment);
calculateAttachment(player);
}
protected void unregisterPlayer(Player player) {
if (permissions.containsKey(player.getName())) {
try {
player.removeAttachment(permissions.get(player.getName()));
}
catch (IllegalArgumentException ex) {
debug("Unregistering " + player.getName() + ": player did not have attachment");
}
permissions.remove(player.getName());
} else {
debug("Unregistering " + player.getName() + ": was not registered");
}
}
protected void refreshPermissions() {
saveConfig();
for (String player : permissions.keySet()) {
PermissionAttachment attachment = permissions.get(player);
for (String key : attachment.getPermissions().keySet()) {
attachment.unsetPermission(key);
}
calculateAttachment(getServer().getPlayer(player));
}
}
protected ConfigurationSection getNode(String node) {
for (String entry : getConfig().getKeys(true)) {
if (node.equalsIgnoreCase(entry) && getConfig().isConfigurationSection(entry)) {
return getConfig().getConfigurationSection(entry);
}
}
return null;
}
protected void createNode(String node) {
ConfigurationSection sec = getConfig();
for (String piece : node.split("/")) {
ConfigurationSection sec2 = getNode(sec == getConfig() ? piece : sec.getCurrentPath() + "/" + piece);
if (sec2 == null) {
sec2 = sec.createSection(piece);
}
sec = sec2;
}
}
protected HashMap<String, Boolean> getAllPerms(String desc, String path) {
HashMap<String, Boolean> result = new HashMap<String, Boolean>();
ConfigurationSection node = getNode(path);
int failures = 0;
String firstFailure = "";
// Make an attempt to autofix incorrect nesting
boolean fixed = false, fixedNow = true;
while (fixedNow) {
fixedNow = false;
for (String key : node.getKeys(true)) {
if (node.isBoolean(key) && key.contains("/")) {
node.set(key.replace("/", "."), node.getBoolean(key));
node.set(key, null);
fixed = fixedNow = true;
} else if (node.isConfigurationSection(key) && node.getConfigurationSection(key).getKeys(true).size() == 0) {
node.set(key, null);
fixed = fixedNow = true;
}
}
}
if (fixed) {
getLogger().info("Fixed broken nesting in " + desc + ".");
saveConfig();
}
// Do the actual getting of permissions
for (String key : node.getKeys(false)) {
if (node.isBoolean(key)) {
result.put(key, node.getBoolean(key));
} else {
++failures;
if (firstFailure.length() == 0) {
firstFailure = key;
}
}
}
if (failures == 1) {
getLogger().warning("In " + desc + ": " + firstFailure + " is non-boolean.");
} else if (failures > 1) {
getLogger().warning("In " + desc + ": " + firstFailure + " is non-boolean (+" + (failures-1) + " more).");
}
return result;
}
protected void debug(String message) {
if (getConfig().getBoolean("debug", false)) {
getLogger().info("Debug: " + message);
}
}
protected void calculateAttachment(Player player) {
if (player == null) {
return;
}
PermissionAttachment attachment = permissions.get(player.getName());
if (attachment == null) {
debug("Calculating permissions on " + player.getName() + ": attachment was null");
return;
}
for (String key : attachment.getPermissions().keySet()) {
attachment.unsetPermission(key);
}
for (Map.Entry<String, Boolean> entry : calculatePlayerPermissions(player.getName().toLowerCase(), player.getWorld().getName()).entrySet()) {
attachment.setPermission(entry.getKey(), entry.getValue());
}
player.recalculatePermissions();
}
// -- Private stuff
private Map<String, Boolean> calculatePlayerPermissions(String player, String world) {
if (getNode("users/" + player) == null) {
return calculateGroupPermissions("default", world);
}
Map<String, Boolean> perms = getNode("users/" + player + "/permissions") == null ?
new HashMap<String, Boolean>() :
getAllPerms("user " + player, "users/" + player + "/permissions");
if (getNode("users/" + player + "/worlds/" + world) != null) {
for (Map.Entry<String, Boolean> entry : getAllPerms("user " + player + " world " + world, "users/" + player + "/worlds/" + world).entrySet()) {
// No containskey; world overrides non-world
perms.put(entry.getKey(), entry.getValue());
}
}
for (String group : getNode("users/" + player).getStringList("groups")) {
for (Map.Entry<String, Boolean> entry : calculateGroupPermissions(group, world).entrySet()) {
if (!perms.containsKey(entry.getKey())) { // User overrides group
perms.put(entry.getKey(), entry.getValue());
}
}
}
return perms;
}
private Map<String, Boolean> calculateGroupPermissions(String group, String world) {
if (getNode("groups/" + group) == null) {
return new HashMap<String, Boolean>();
}
Map<String, Boolean> perms = getNode("groups/" + group + "/permissions") == null ?
new HashMap<String, Boolean>() :
getAllPerms("group " + group, "groups/" + group + "/permissions");
if (getNode("groups/" + group + "/worlds/" + world) != null) {
for (Map.Entry<String, Boolean> entry : getAllPerms("group " + group + " world " + world, "groups/" + group + "/worlds/" + world).entrySet()) {
// No containskey; world overrides non-world
perms.put(entry.getKey(), entry.getValue());
}
}
for (String parent : getNode("groups/" + group).getStringList("inheritance")) {
for (Map.Entry<String, Boolean> entry : calculateGroupPermissions(parent, world).entrySet()) {
if (!perms.containsKey(entry.getKey())) { // Children override permissions
perms.put(entry.getKey(), entry.getValue());
}
}
}
return perms;
}
}