From ae31156f722a7ac7d61f95633643909f75cdee12 Mon Sep 17 00:00:00 2001 From: 502647092 Date: Tue, 1 Mar 2016 11:15:02 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=BF=E7=94=A8YUMC=E7=BB=9F=E8=AE=A1?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 502647092 --- .classpath | 31 + .gitignore | 38 + .project | 23 + pom.xml | 78 + .../groupmanager/GMConfiguration.java | 208 ++ .../anjocaido/groupmanager/GlobalGroups.java | 483 ++++ .../anjocaido/groupmanager/GroupManager.java | 2237 +++++++++++++++++ .../Tasks/BukkitPermsUpdateTask.java | 29 + .../anjocaido/groupmanager/data/DataUnit.java | 205 ++ .../anjocaido/groupmanager/data/Group.java | 197 ++ .../groupmanager/data/GroupVariables.java | 97 + .../org/anjocaido/groupmanager/data/User.java | 323 +++ .../groupmanager/data/UserVariables.java | 56 + .../groupmanager/data/Variables.java | 213 ++ .../dataholder/GroupsDataHolder.java | 136 + .../dataholder/OverloadedWorldHolder.java | 220 ++ .../dataholder/UsersDataHolder.java | 118 + .../dataholder/WorldDataHolder.java | 1466 +++++++++++ .../dataholder/worlds/WorldsHolder.java | 794 ++++++ .../groupmanager/events/GMGroupEvent.java | 92 + .../groupmanager/events/GMSystemEvent.java | 66 + .../groupmanager/events/GMUserEvent.java | 94 + .../groupmanager/events/GMWorldListener.java | 61 + .../events/GroupManagerEventHandler.java | 82 + .../permissions/AnjoPermissionsHandler.java | 1353 ++++++++++ .../permissions/BukkitPermissions.java | 497 ++++ .../PermissionsReaderInterface.java | 255 ++ .../groupmanager/utils/GMLoggerHandler.java | 27 + .../utils/GroupManagerPermissions.java | 56 + .../utils/PermissionCheckResult.java | 67 + .../utils/StringPermissionComparator.java | 52 + .../anjocaido/groupmanager/utils/Tasks.java | 175 ++ src/main/resources/Changelog.txt | 233 ++ src/main/resources/config.yml | 50 + src/main/resources/globalgroups.yml | 329 +++ src/main/resources/groups.yml | 74 + src/main/resources/plugin.yml | 184 ++ src/main/resources/users.yml | 18 + 38 files changed, 10717 insertions(+) create mode 100644 .classpath create mode 100644 .gitignore create mode 100644 .project create mode 100644 pom.xml create mode 100644 src/main/java/org/anjocaido/groupmanager/GMConfiguration.java create mode 100644 src/main/java/org/anjocaido/groupmanager/GlobalGroups.java create mode 100644 src/main/java/org/anjocaido/groupmanager/GroupManager.java create mode 100644 src/main/java/org/anjocaido/groupmanager/Tasks/BukkitPermsUpdateTask.java create mode 100644 src/main/java/org/anjocaido/groupmanager/data/DataUnit.java create mode 100644 src/main/java/org/anjocaido/groupmanager/data/Group.java create mode 100644 src/main/java/org/anjocaido/groupmanager/data/GroupVariables.java create mode 100644 src/main/java/org/anjocaido/groupmanager/data/User.java create mode 100644 src/main/java/org/anjocaido/groupmanager/data/UserVariables.java create mode 100644 src/main/java/org/anjocaido/groupmanager/data/Variables.java create mode 100644 src/main/java/org/anjocaido/groupmanager/dataholder/GroupsDataHolder.java create mode 100644 src/main/java/org/anjocaido/groupmanager/dataholder/OverloadedWorldHolder.java create mode 100644 src/main/java/org/anjocaido/groupmanager/dataholder/UsersDataHolder.java create mode 100644 src/main/java/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java create mode 100644 src/main/java/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java create mode 100644 src/main/java/org/anjocaido/groupmanager/events/GMGroupEvent.java create mode 100644 src/main/java/org/anjocaido/groupmanager/events/GMSystemEvent.java create mode 100644 src/main/java/org/anjocaido/groupmanager/events/GMUserEvent.java create mode 100644 src/main/java/org/anjocaido/groupmanager/events/GMWorldListener.java create mode 100644 src/main/java/org/anjocaido/groupmanager/events/GroupManagerEventHandler.java create mode 100644 src/main/java/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java create mode 100644 src/main/java/org/anjocaido/groupmanager/permissions/BukkitPermissions.java create mode 100644 src/main/java/org/anjocaido/groupmanager/permissions/PermissionsReaderInterface.java create mode 100644 src/main/java/org/anjocaido/groupmanager/utils/GMLoggerHandler.java create mode 100644 src/main/java/org/anjocaido/groupmanager/utils/GroupManagerPermissions.java create mode 100644 src/main/java/org/anjocaido/groupmanager/utils/PermissionCheckResult.java create mode 100644 src/main/java/org/anjocaido/groupmanager/utils/StringPermissionComparator.java create mode 100644 src/main/java/org/anjocaido/groupmanager/utils/Tasks.java create mode 100644 src/main/resources/Changelog.txt create mode 100644 src/main/resources/config.yml create mode 100644 src/main/resources/globalgroups.yml create mode 100644 src/main/resources/groups.yml create mode 100644 src/main/resources/plugin.yml create mode 100644 src/main/resources/users.yml diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..bea39b1 --- /dev/null +++ b/.classpath @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7876f5f --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# Eclipse stuff +/.settings + +# netbeans +/nbproject + +# we use maven! +/build.xml + +# maven +/target +/repo + +# vim +.*.sw[a-p] + +# various other potential build files +/build +/bin +/dist +/manifest.mf + +/world + +# Mac filesystem dust +*.DS_Store + +# intellij +*.iml +*.ipr +*.iws +.idea/ + +# Project Stuff +/src/main/resources/Soulbound + +# Atlassian Stuff +/atlassian-ide-plugin.xml \ No newline at end of file diff --git a/.project b/.project new file mode 100644 index 0000000..0f85a5a --- /dev/null +++ b/.project @@ -0,0 +1,23 @@ + + + GroupManager + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + org.eclipse.jdt.core.javanature + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..0cd9ecc --- /dev/null +++ b/pom.xml @@ -0,0 +1,78 @@ + + 4.0.0 + org.anjocaido + GroupManager + 2.3 + + ${project.name} + + + src/main/resources + true + + + + + maven-compiler-plugin + 3.1 + + 1.7 + 1.7 + + + + org.apache.maven.plugins + maven-shade-plugin + 2.3 + + false + true + + + cn.citycraft:PluginHelper + + + + + cn.citycraft.PluginHelper + ${project.groupId}.${project.artifactId} + + + + + + package + + shade + + + + + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/groups/public/ + + + citycraft-repo + ${jenkins.url}/plugin/repository/everything/ + + + + + org.spigotmc + spigot-api + jar + 1.8.8-R0.1-SNAPSHOT + + + cn.citycraft + PluginHelper + jar + 1.0 + + + \ No newline at end of file diff --git a/src/main/java/org/anjocaido/groupmanager/GMConfiguration.java b/src/main/java/org/anjocaido/groupmanager/GMConfiguration.java new file mode 100644 index 0000000..1a51112 --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/GMConfiguration.java @@ -0,0 +1,208 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.anjocaido.groupmanager; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; + +import org.anjocaido.groupmanager.utils.Tasks; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.reader.UnicodeReader; + +/** + * + * @author gabrielcouto + */ +public class GMConfiguration { + + private boolean allowCommandBlocks = false; + private boolean opOverride = true; + private boolean toggleValidate = true; + private Integer saveInterval = 10; + private Integer backupDuration = 24; + private String loggerLevel = "OFF"; + private Map mirrorsMap; + + private GroupManager plugin; + private Map GMconfig; + + public GMConfiguration(GroupManager plugin) { + + this.plugin = plugin; + + /* + * Set defaults + */ + allowCommandBlocks = false; + opOverride = true; + toggleValidate = true; + saveInterval = 10; + backupDuration = 24; + loggerLevel = "OFF"; + + load(); + } + + @SuppressWarnings("unchecked") + public void load() { + + if (!plugin.getDataFolder().exists()) { + plugin.getDataFolder().mkdirs(); + } + + File configFile = new File(plugin.getDataFolder(), "config.yml"); + + if (!configFile.exists()) { + try { + Tasks.copy(plugin.getResourceAsStream("config.yml"), configFile); + } catch (IOException ex) { + GroupManager.logger.log(Level.SEVERE, "Error creating a new config.yml", ex); + } + } + + Yaml configYAML = new Yaml(new SafeConstructor()); + + try { + FileInputStream configInputStream = new FileInputStream(configFile); + GMconfig = (Map) configYAML.load(new UnicodeReader(configInputStream)); + configInputStream.close(); + + } catch (Exception ex) { + throw new IllegalArgumentException("The following file couldn't pass on Parser.\n" + configFile.getPath(), ex); + } + + /* + * Read our config settings and store them for reading later. + */ + try { + Map config = getElement("config", getElement("settings", GMconfig)); + + try { + allowCommandBlocks = (Boolean) config.get("allow_commandblocks"); + } catch (Exception ex) { + GroupManager.logger.log(Level.SEVERE, "Missing or corrupt 'allow_commandblocks' node. Using default settings", ex); + } + + try { + opOverride = (Boolean) config.get("opOverrides"); + } catch (Exception ex) { + GroupManager.logger.log(Level.SEVERE, "Missing or corrupt 'opOverrides' node. Using default settings", ex); + } + + try { + toggleValidate = (Boolean) config.get("validate_toggle"); + } catch (Exception ex) { + GroupManager.logger.log(Level.SEVERE, "Missing or corrupt 'validate_toggle' node. Using default settings", ex); + } + + /* + * data node for save/backup timers. + */ + try { + Map save = getElement("save", getElement("data", getElement("settings", GMconfig))); + + try { + saveInterval = (Integer) save.get("minutes"); + } catch (Exception ex) { + GroupManager.logger.log(Level.SEVERE, "Missing or corrupt 'minutes' node. Using default setting", ex); + } + + try { + backupDuration = (Integer) save.get("hours"); + } catch (Exception ex) { + GroupManager.logger.log(Level.SEVERE, "Missing or corrupt 'hours' node. Using default setting", ex); + } + + } catch (Exception ex) { + GroupManager.logger.log(Level.SEVERE, "Missing or corrupt 'data' node. Using default settings", ex); + } + + Object level = ((Map) getElement("settings", GMconfig).get("logging")).get("level"); + if (level instanceof String) + loggerLevel = (String) level; + + /* + * Store our mirrors map for parsing later. + */ + mirrorsMap = (Map) ((Map) GMconfig.get("settings")).get("mirrors"); + + if (mirrorsMap == null) + throw new Exception(); + + } catch (Exception ex) { + /* + * Flag the error and use defaults + */ + GroupManager.logger.log(Level.SEVERE, "There are errors in your config.yml. Using default settings", ex); + + mirrorsMap = new HashMap(); + } + // Setup defaults + adjustLoggerLevel(); + plugin.setValidateOnlinePlayer(isToggleValidate()); + } + + @SuppressWarnings("unchecked") + private Map getElement(String element, Map map) { + + if (!map.containsKey(element)) { + throw new IllegalArgumentException("The config.yml has no '" + element + ".\n"); + } + + return (Map) map.get(element); + + } + + public boolean isAllowCommandBlocks() { + + return allowCommandBlocks; + } + + public boolean isOpOverride() { + + return opOverride; + } + + public boolean isToggleValidate() { + + return toggleValidate; + } + + public Integer getSaveInterval() { + + return saveInterval; + } + + public Integer getBackupDuration() { + + return backupDuration; + } + + public void adjustLoggerLevel() { + + try { + GroupManager.logger.setLevel(Level.parse(loggerLevel)); + return; + } catch (Exception e) { + } + + GroupManager.logger.setLevel(Level.INFO); + } + + public Map getMirrorsMap() { + + if (!mirrorsMap.isEmpty()) { + return mirrorsMap; + } + return null; + + } + +} diff --git a/src/main/java/org/anjocaido/groupmanager/GlobalGroups.java b/src/main/java/org/anjocaido/groupmanager/GlobalGroups.java new file mode 100644 index 0000000..6c92046 --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/GlobalGroups.java @@ -0,0 +1,483 @@ +package org.anjocaido.groupmanager; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; + +import org.anjocaido.groupmanager.data.Group; +import org.anjocaido.groupmanager.events.GMGroupEvent; +import org.anjocaido.groupmanager.utils.PermissionCheckResult; +import org.anjocaido.groupmanager.utils.Tasks; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.reader.UnicodeReader; + +/** + * @author ElgarL + * + */ +public class GlobalGroups { + + private GroupManager plugin; + // private Yaml GGroups; + + private final Map groups = Collections.synchronizedMap(new HashMap()); + + protected long timeStampGroups = 0; + protected boolean haveGroupsChanged = false; + protected File GlobalGroupsFile = null; + + public GlobalGroups(GroupManager plugin) { + + this.plugin = plugin; + load(); + } + + /** + * @return the haveGroupsChanged + */ + public boolean haveGroupsChanged() { + + if (this.haveGroupsChanged) { + return true; + } + synchronized (groups) { + for (Group g : groups.values()) { + if (g.isChanged()) { + return true; + } + } + } + return false; + } + + /** + * @return the timeStampGroups + */ + public long getTimeStampGroups() { + + return timeStampGroups; + } + + /** + * @param timeStampGroups + * the timeStampGroups to set + */ + protected void setTimeStampGroups(long timeStampGroups) { + + this.timeStampGroups = timeStampGroups; + } + + /** + * @param haveGroupsChanged + * the haveGroupsChanged to set + */ + public void setGroupsChanged(boolean haveGroupsChanged) { + + this.haveGroupsChanged = haveGroupsChanged; + } + + @SuppressWarnings("unchecked") + public void load() { + + Yaml GGroupYAML = new Yaml(new SafeConstructor()); + Map GGroups; + + GroupManager.setLoaded(false); + + // READ globalGroups FILE + if (GlobalGroupsFile == null) + GlobalGroupsFile = new File(plugin.getDataFolder(), "globalgroups.yml"); + + if (!GlobalGroupsFile.exists()) { + try { + // Create a new file if it doesn't exist. + Tasks.copy(plugin.getResourceAsStream("globalgroups.yml"), GlobalGroupsFile); + } catch (IOException ex) { + GroupManager.logger.log(Level.SEVERE, null, ex); + } + } + + /* + * Load the YAML file. + */ + try { + FileInputStream groupsInputStream = new FileInputStream(GlobalGroupsFile); + GGroups = (Map) GGroupYAML.load(new UnicodeReader(groupsInputStream)); + groupsInputStream.close(); + } catch (Exception ex) { + throw new IllegalArgumentException("The following file couldn't pass on Parser.\n" + GlobalGroupsFile.getPath(), ex); + } + + // Clear out old groups + resetGlobalGroups(); + + if (!GGroups.keySet().isEmpty()) { + // Read all global groups + Map allGroups = new HashMap(); + + try { + allGroups = (Map) GGroups.get("groups"); + } catch (Exception ex) { + // ex.printStackTrace(); + throw new IllegalArgumentException("Your " + GlobalGroupsFile.getPath() + " file is invalid. See console for details.", ex); + } + + // Load each groups permissions list. + if (allGroups != null) { + + Iterator groupItr = allGroups.keySet().iterator(); + String groupName; + Integer groupCount = 0; + + /* + * loop each group entry + * and read it's data. + */ + while (groupItr.hasNext()) { + try { + groupCount++; + // Attempt to fetch the next group name. + groupName = groupItr.next(); + } catch (Exception ex) { + throw new IllegalArgumentException("Invalid group name for GlobalGroup entry (" + groupCount + ") in file: " + GlobalGroupsFile.getPath(), ex); + } + + /* + * Create a new group with this name. + */ + Group newGroup = new Group(groupName.toLowerCase()); + Object element; + + // Permission nodes + try { + element = ((Map) allGroups.get(groupName)).get("permissions"); + } catch (Exception ex) { + throw new IllegalArgumentException("The GlobalGroup ' " + groupName + "' is formatted incorrectly: ", ex); + } + + if (element != null) + if (element instanceof List) { + try { + for (String node : (List) element) { + if ((node != null) && !node.isEmpty()) + newGroup.addPermission(node); + } + } catch (ClassCastException ex) { + throw new IllegalArgumentException("Invalid permission node for global group: " + groupName, ex); + } + } else if (element instanceof String) { + if ((element != null) && !((String) element).isEmpty()) + newGroup.addPermission((String) element); + } else + throw new IllegalArgumentException("Unknown type of permission node for global group: " + groupName); + + // // Info nodes + // try { + // element = ((Map)allGroups.get(groupName)).get("info"); + // } catch ( Exception ex) { + // throw new IllegalArgumentException("The GlobalGroup ' " + groupName + "' is formatted incorrectly: ", ex); + // } + // + // if (element != null) + // if (element instanceof MemorySection) { + // Map vars = new HashMap(); + // for (String key : ((MemorySection) element).getKeys(false)) { + // vars.put(key, ((MemorySection) element).get(key)); + // } + // newGroup.setVariables(vars); + // } else + // throw new IllegalArgumentException("Unknown type of info node for global group: " + groupName); + + // Push a new group + addGroup(newGroup); + } + } + + removeGroupsChangedFlag(); + } + + setTimeStampGroups(GlobalGroupsFile.lastModified()); + GroupManager.setLoaded(true); + // GlobalGroupsFile = null; + } + + /** + * Write the globalgroups.yml file + */ + + public void writeGroups(boolean overwrite) { + + // File GlobalGroupsFile = new File(plugin.getDataFolder(), "globalgroups.yml"); + + if (haveGroupsChanged()) { + if (overwrite || (!overwrite && (getTimeStampGroups() >= GlobalGroupsFile.lastModified()))) { + Map root = new HashMap(); + + Map groupsMap = new HashMap(); + root.put("groups", groupsMap); + synchronized (groups) { + for (String groupKey : groups.keySet()) { + Group group = groups.get(groupKey); + + // Group header + Map aGroupMap = new HashMap(); + groupsMap.put(group.getName(), aGroupMap); + + // // Info nodes + // Map infoMap = new HashMap(); + // aGroupMap.put("info", infoMap); + // + // for (String infoKey : group.getVariables().getVarKeyList()) { + // infoMap.put(infoKey, group.getVariables().getVarObject(infoKey)); + // } + + // Permission nodes + aGroupMap.put("permissions", group.getPermissionList()); + } + } + + if (!root.isEmpty()) { + DumperOptions opt = new DumperOptions(); + opt.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + final Yaml yaml = new Yaml(opt); + try { + yaml.dump(root, new OutputStreamWriter(new FileOutputStream(GlobalGroupsFile), "UTF-8")); + } catch (UnsupportedEncodingException ex) { + } catch (FileNotFoundException ex) { + } + } + setTimeStampGroups(GlobalGroupsFile.lastModified()); + } else { + // Newer file found. + GroupManager.logger.log(Level.WARNING, "Newer GlobalGroups file found, but we have local changes!"); + throw new IllegalStateException("Unable to save unless you issue a '/mansave force'"); + } + removeGroupsChangedFlag(); + } else { + // Check for newer file as no local changes. + if (getTimeStampGroups() < GlobalGroupsFile.lastModified()) { + System.out.print("Newer GlobalGroups file found (Loading changes)!"); + // Backup GlobalGroups file + backupFile(); + load(); + } + } + + } + + /** + * Backup the BlobalGroups file + * + * @param w + */ + private void backupFile() { + + File backupFile = new File(plugin.getBackupFolder(), "bkp_ggroups_" + Tasks.getDateString() + ".yml"); + try { + Tasks.copy(GlobalGroupsFile, backupFile); + } catch (IOException ex) { + GroupManager.logger.log(Level.SEVERE, null, ex); + } + } + + /** + * Adds a group, or replaces an existing one. + * + * @param groupToAdd + */ + public void addGroup(Group groupToAdd) { + + // Create a new group if it already exists + if (hasGroup(groupToAdd.getName())) { + groupToAdd = groupToAdd.clone(); + removeGroup(groupToAdd.getName()); + } + + newGroup(groupToAdd); + haveGroupsChanged = true; + if (GroupManager.isLoaded()) + GroupManager.getGMEventHandler().callEvent(groupToAdd, GMGroupEvent.Action.GROUP_ADDED); + } + + /** + * Creates a new group if it doesn't already exist. + * + * @param newGroup + */ + public Group newGroup(Group newGroup) { + + // Push a new group + if (!groups.containsKey(newGroup.getName().toLowerCase())) { + groups.put(newGroup.getName().toLowerCase(), newGroup); + this.setGroupsChanged(true); + return newGroup; + } + return null; + } + + /** + * Delete a group if it exist. + * + * @param groupName + */ + public boolean removeGroup(String groupName) { + + // Push a new group + if (groups.containsKey(groupName.toLowerCase())) { + groups.remove(groupName.toLowerCase()); + this.setGroupsChanged(true); + if (GroupManager.isLoaded()) + GroupManager.getGMEventHandler().callEvent(groupName.toLowerCase(), GMGroupEvent.Action.GROUP_REMOVED); + return true; + } + return false; + } + + /** + * Returns true if the Global Group exists in the globalgroups.yml + * + * @param groupName + * @return true if the group exists + */ + public boolean hasGroup(String groupName) { + + return groups.containsKey(groupName.toLowerCase()); + } + + /** + * Returns true if the group has the correct permission node. + * + * @param groupName + * @param permissionNode + * @return true if node exists + */ + public boolean hasPermission(String groupName, String permissionNode) { + + if (!hasGroup(groupName)) + return false; + + return groups.get(groupName.toLowerCase()).hasSamePermissionNode(permissionNode); + + } + + /** + * Returns a PermissionCheckResult of the permission node for the group to + * be tested against. + * + * @param groupName + * @param permissionNode + * @return PermissionCheckResult object + */ + public PermissionCheckResult checkPermission(String groupName, String permissionNode) { + + PermissionCheckResult result = new PermissionCheckResult(); + result.askedPermission = permissionNode; + result.resultType = PermissionCheckResult.Type.NOTFOUND; + + if (!hasGroup(groupName)) + return result; + + Group tempGroup = groups.get(groupName.toLowerCase()); + + if (tempGroup.hasSamePermissionNode(permissionNode)) + result.resultType = PermissionCheckResult.Type.FOUND; + if (tempGroup.hasSamePermissionNode("-" + permissionNode)) + result.resultType = PermissionCheckResult.Type.NEGATION; + if (tempGroup.hasSamePermissionNode("+" + permissionNode)) + result.resultType = PermissionCheckResult.Type.EXCEPTION; + + return result; + } + + /** + * Returns a List of all permission nodes for this group null if none + * + * @param groupName + * @return List of all group names + */ + public List getGroupsPermissions(String groupName) { + + if (!hasGroup(groupName)) + return null; + + return groups.get(groupName.toLowerCase()).getPermissionList(); + } + + /** + * Returns a Set of all global group names. + * + * @return Set containing all group names. + */ + /* + * public Set getGlobalGroups() { + * + * return groups.keySet(); + * } + */ + + /** + * Resets GlobalGroups. + */ + public void resetGlobalGroups() { + this.groups.clear(); + } + + /** + * + * @return a collection of the groups + */ + public Group[] getGroupList() { + synchronized (groups) { + return groups.values().toArray(new Group[0]); + } + } + + /** + * Returns the Global Group or null if it doesn't exist. + * + * @param groupName + * @return Group object + */ + public Group getGroup(String groupName) { + + if (!hasGroup(groupName)) + return null; + + return groups.get(groupName.toLowerCase()); + + } + + /** + * @return the globalGroupsFile + */ + public File getGlobalGroupsFile() { + + return GlobalGroupsFile; + } + + /** + * + */ + public void removeGroupsChangedFlag() { + + setGroupsChanged(false); + synchronized (groups) { + for (Group g : groups.values()) { + g.flagAsSaved(); + } + } + } + +} \ No newline at end of file diff --git a/src/main/java/org/anjocaido/groupmanager/GroupManager.java b/src/main/java/org/anjocaido/groupmanager/GroupManager.java new file mode 100644 index 0000000..68dd8ee --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/GroupManager.java @@ -0,0 +1,2237 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.anjocaido.groupmanager; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.anjocaido.groupmanager.Tasks.BukkitPermsUpdateTask; +import org.anjocaido.groupmanager.data.Group; +import org.anjocaido.groupmanager.data.User; +import org.anjocaido.groupmanager.data.Variables; +import org.anjocaido.groupmanager.dataholder.OverloadedWorldHolder; +import org.anjocaido.groupmanager.dataholder.worlds.WorldsHolder; +import org.anjocaido.groupmanager.events.GMSystemEvent; +import org.anjocaido.groupmanager.events.GMWorldListener; +import org.anjocaido.groupmanager.events.GroupManagerEventHandler; +import org.anjocaido.groupmanager.permissions.AnjoPermissionsHandler; +import org.anjocaido.groupmanager.permissions.BukkitPermissions; +import org.anjocaido.groupmanager.utils.GMLoggerHandler; +import org.anjocaido.groupmanager.utils.GroupManagerPermissions; +import org.anjocaido.groupmanager.utils.PermissionCheckResult; +import org.anjocaido.groupmanager.utils.Tasks; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.OfflinePlayer; +import org.bukkit.block.Block; +import org.bukkit.command.BlockCommandSender; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.ServicePriority; +import org.bukkit.plugin.java.JavaPlugin; + +import cn.citycraft.PluginHelper.utils.VersionChecker; + +/** + * + * @author gabrielcouto, ElgarL + */ +public class GroupManager extends JavaPlugin { + + private static boolean isLoaded = false; + protected static GlobalGroups globalGroups; + private static GroupManagerEventHandler GMEventHandler; + public static BukkitPermissions BukkitPermissions; + private static GMWorldListener WorldEvents; + public static final Logger logger = Logger.getLogger(GroupManager.class.getName()); + private File backupFolder; + + private Runnable commiter; + private ScheduledThreadPoolExecutor scheduler; + + private Map> overloadedUsers = new HashMap>(); + + private Map selectedWorlds = new HashMap(); + + private WorldsHolder worldsHolder; + private boolean validateOnlinePlayer = true; + protected GMConfiguration config; + private GMLoggerHandler ch; + + // PERMISSIONS FOR COMMAND BEING LOADED + private OverloadedWorldHolder dataHolder = null; + private AnjoPermissionsHandler permissionHandler = null; + + private String lastError = ""; + + public static GlobalGroups getGlobalGroups() { + + return globalGroups; + + } + + public static GroupManagerEventHandler getGMEventHandler() { + + return GMEventHandler; + } + + public static boolean isLoaded() { + + return isLoaded; + } + + /** + * Send confirmation of a group change. using permission nodes... + * + * groupmanager.notify.self groupmanager.notify.other + * + * @param name + * @param msg + */ + public static void notify(final String name, final String msg) { + + final Player player = Bukkit.getServer().getPlayerExact(name); + + for (final Player test : Bukkit.getServer().getOnlinePlayers()) { + if (!test.equals(player)) { + if (test.hasPermission("groupmanager.notify.other")) + test.sendMessage(ChatColor.YELLOW + name + " was" + msg); + } else if ((player != null) && ((player.hasPermission("groupmanager.notify.self")) || (player.hasPermission("groupmanager.notify.other")))) + player.sendMessage(ChatColor.YELLOW + "You were" + msg); + } + + } + + public static void setGMEventHandler(final GroupManagerEventHandler gMEventHandler) { + + GMEventHandler = gMEventHandler; + } + + public static void setLoaded(final boolean isLoaded) { + + GroupManager.isLoaded = isLoaded; + } + + public void disableScheduler() { + + if (scheduler != null) { + try { + scheduler.setContinueExistingPeriodicTasksAfterShutdownPolicy(false); + scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + scheduler.shutdown(); + } catch (final Exception e) { + } + scheduler = null; + GroupManager.logger.info("Scheduled Data Saving is disabled!"); + } + } + + public void enableScheduler() { + + if (worldsHolder != null) { + disableScheduler(); + commiter = new Runnable() { + + @Override + public void run() { + try { + if (worldsHolder.saveChanges(false)) + GroupManager.logger.log(Level.INFO, " Data files refreshed."); + } catch (final IllegalStateException ex) { + GroupManager.logger.log(Level.WARNING, ex.getMessage()); + } + } + }; + scheduler = new ScheduledThreadPoolExecutor(1); + final long minutes = getGMConfig().getSaveInterval(); + if (minutes > 0) { + scheduler.scheduleAtFixedRate(commiter, minutes, minutes, TimeUnit.MINUTES); + GroupManager.logger.info("Scheduled Data Saving is set for every " + minutes + " minutes!"); + } else + GroupManager.logger.info("Scheduled Data Saving is Disabled!"); + + GroupManager.logger.info("Backups will be retained for " + getGMConfig().getBackupDuration() + " hours!"); + } + } + + /** + * @return the backupFolder + */ + public File getBackupFolder() { + + return backupFolder; + } + + /** + * @return the config + */ + public GMConfiguration getGMConfig() { + + return config; + } + + public InputStream getResourceAsStream(final String fileName) { + + return this.getClassLoader().getResourceAsStream(fileName); + } + + public WorldsHolder getWorldsHolder() { + + return worldsHolder; + } + + /** + * @return the validateOnlinePlayer + */ + public boolean isValidateOnlinePlayer() { + + return validateOnlinePlayer; + } + + /** + * Called when a command registered by this plugin is received. + * + * @param sender + * @param cmd + * @param args + */ + @Override + public boolean onCommand(final CommandSender sender, final Command cmd, final String commandLabel, final String[] args) { + + boolean playerCanDo = false; + boolean isConsole = false; + Player senderPlayer = null, targetPlayer = null; + Group senderGroup = null; + User senderUser = null; + boolean isOpOverride = config.isOpOverride(); + final boolean isAllowCommandBlocks = config.isAllowCommandBlocks(); + + // PREVENT GM COMMANDS BEING USED ON COMMANDBLOCKS + if (sender instanceof BlockCommandSender && !isAllowCommandBlocks) { + final Block block = ((BlockCommandSender) sender).getBlock(); + GroupManager.logger.warning(ChatColor.RED + "GM Commands can not be called from CommandBlocks"); + GroupManager.logger.warning(ChatColor.RED + "Location: " + ChatColor.GREEN + block.getWorld().getName() + ", " + block.getX() + ", " + block.getY() + ", " + block.getZ()); + return true; + } + + // DETERMINING PLAYER INFORMATION + if (sender instanceof Player) { + senderPlayer = (Player) sender; + + if (!lastError.isEmpty() && !commandLabel.equalsIgnoreCase("manload")) { + sender.sendMessage(ChatColor.RED + "All commands are locked due to an error. " + ChatColor.BOLD + "" + ChatColor.UNDERLINE + "Check plugins/groupmanager/error.log or console" + ChatColor.RESET + "" + ChatColor.RED + + " and then try a '/manload'."); + return true; + } + + senderUser = worldsHolder.getWorldData(senderPlayer).getUser(senderPlayer.getName()); + senderGroup = senderUser.getGroup(); + isOpOverride = (isOpOverride && (senderPlayer.isOp() || worldsHolder.getWorldPermissions(senderPlayer).has(senderPlayer, "groupmanager.op"))); + + if (isOpOverride || worldsHolder.getWorldPermissions(senderPlayer).has(senderPlayer, "groupmanager." + cmd.getName())) { + playerCanDo = true; + } + } else { + + if (!lastError.isEmpty() && !commandLabel.equalsIgnoreCase("manload")) { + sender.sendMessage(ChatColor.RED + "All commands are locked due to an error. " + ChatColor.BOLD + "" + ChatColor.UNDERLINE + "Check plugins/groupmanager/error.log or console" + ChatColor.RESET + "" + ChatColor.RED + + " and then try a '/manload'."); + return true; + } + + isConsole = true; + } + + // PERMISSIONS FOR COMMAND BEING LOADED + dataHolder = null; + permissionHandler = null; + + if (senderPlayer != null) { + dataHolder = worldsHolder.getWorldData(senderPlayer); + } + + final String selectedWorld = selectedWorlds.get(sender.getName()); + if (selectedWorld != null) { + dataHolder = worldsHolder.getWorldData(selectedWorld); + } + + if (dataHolder != null) { + permissionHandler = dataHolder.getPermissionsHandler(); + } + + // VARIABLES USED IN COMMANDS + + int count; + PermissionCheckResult permissionResult = null; + ArrayList removeList = null; + String auxString = null; + List match = null; + User auxUser = null; + Group auxGroup = null; + Group auxGroup2 = null; + + GroupManagerPermissions execCmd = null; + try { + execCmd = GroupManagerPermissions.valueOf(cmd.getName()); + } catch (final Exception e) { + // this error happened once with someone. now im prepared... i think + GroupManager.logger.severe("==================================================="); + GroupManager.logger.severe("= ERROR REPORT START ="); + GroupManager.logger.severe("==================================================="); + GroupManager.logger.severe("= COPY AND PASTE THIS TO A GROUPMANAGER DEVELOPER ="); + GroupManager.logger.severe("==================================================="); + GroupManager.logger.severe(this.getDescription().getName()); + GroupManager.logger.severe(this.getDescription().getVersion()); + GroupManager.logger.severe("An error occured while trying to execute command:"); + GroupManager.logger.severe(cmd.getName()); + GroupManager.logger.severe("With " + args.length + " arguments:"); + for (final String ar : args) { + GroupManager.logger.severe(ar); + } + GroupManager.logger.severe("The field '" + cmd.getName() + "' was not found in enum."); + GroupManager.logger.severe("And could not be parsed."); + GroupManager.logger.severe("FIELDS FOUND IN ENUM:"); + for (final GroupManagerPermissions val : GroupManagerPermissions.values()) { + GroupManager.logger.severe(val.name()); + } + GroupManager.logger.severe("==================================================="); + GroupManager.logger.severe("= ERROR REPORT ENDED ="); + GroupManager.logger.severe("==================================================="); + sender.sendMessage("An error occurred. Ask the admin to take a look at the console."); + } + + if (isConsole || playerCanDo) { + switch (execCmd) { + case manuadd: + + // Validating arguments + if ((args.length != 2) && (args.length != 3)) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manuadd | optional [world])"); + return true; + } + + // Select the relevant world (if specified) + if (args.length == 3) { + dataHolder = worldsHolder.getWorldData(args[2]); + permissionHandler = dataHolder.getPermissionsHandler(); + } + + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + auxGroup = dataHolder.getGroup(args[1]); + if (auxGroup == null) { + sender.sendMessage(ChatColor.RED + "'" + args[1] + "' Group doesnt exist!"); + return false; + } + if (auxGroup.isGlobal()) { + sender.sendMessage(ChatColor.RED + "Players may not be members of GlobalGroups directly."); + return false; + } + + // Validating permissions + if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getLastName(), senderGroup.getName()) : false)) { + sender.sendMessage(ChatColor.RED + "Can't modify a player with the same permissions as you, or higher."); + return true; + } + if (!isConsole && !isOpOverride && (permissionHandler.hasGroupInInheritance(auxGroup, senderGroup.getName()))) { + sender.sendMessage(ChatColor.RED + "The destination group can't be the same as yours, or higher."); + return true; + } + if (!isConsole && !isOpOverride && (!permissionHandler.inGroup(senderUser.getLastName(), auxUser.getGroupName()) || !permissionHandler.inGroup(senderUser.getLastName(), auxGroup.getName()))) { + sender.sendMessage(ChatColor.RED + "You can't modify a player involving a group that you don't inherit."); + return true; + } + + // Seems OK + auxUser.setGroup(auxGroup); + if (!sender.hasPermission("groupmanager.notify.other") || (isConsole)) + sender.sendMessage(ChatColor.YELLOW + "You changed player '" + auxUser.getLastName() + "' group to '" + auxGroup.getName() + "' in world '" + dataHolder.getName() + "'."); + + return true; + + case manudel: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 1) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manudel )"); + return true; + } + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + // Validating permission + if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getLastName(), senderGroup.getName()) : false)) { + sender.sendMessage(ChatColor.RED + "You can't modify a player with same permissions as you, or higher."); + return true; + } + // Seems OK + dataHolder.removeUser(auxUser.getLastName()); + sender.sendMessage(ChatColor.YELLOW + "You changed player '" + auxUser.getLastName() + "' to default settings."); + + // If the player is online, this will create new data for the user. + targetPlayer = this.getServer().getPlayer(auxUser.getLastName()); + if (targetPlayer != null) + BukkitPermissions.updatePermissions(targetPlayer); + + return true; + + case manuaddsub: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) { + sender.sendMessage(ChatColor.RED + "Couldn't retrieve your world. World selection is needed."); + sender.sendMessage(ChatColor.RED + "Use /manselect "); + return true; + } + } + // Validating arguments + if (args.length != 2) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manuaddsub )"); + return true; + } + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + auxGroup = dataHolder.getGroup(args[1]); + if (auxGroup == null) { + sender.sendMessage(ChatColor.RED + "'" + args[1] + "' Group doesnt exist!"); + return true; + } + // Validating permission + if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getLastName(), senderGroup.getName()) : false)) { + sender.sendMessage(ChatColor.RED + "You can't modify a player with same permissions as you, or higher."); + return true; + } + if (!isConsole && !isOpOverride && (permissionHandler.hasGroupInInheritance(auxGroup, senderGroup.getName()))) { + sender.sendMessage(ChatColor.RED + "The sub-group can't be the same as yours, or higher."); + return true; + } + if (!isConsole && !isOpOverride && (!permissionHandler.inGroup(senderUser.getLastName(), auxUser.getGroupName()) || !permissionHandler.inGroup(senderUser.getLastName(), auxGroup.getName()))) { + sender.sendMessage(ChatColor.RED + "You can't modify a player involving a group that you don't inherit."); + return true; + } + // Seems OK + if (auxUser.addSubGroup(auxGroup)) + sender.sendMessage(ChatColor.YELLOW + "You added subgroup '" + auxGroup.getName() + "' to player '" + auxUser.getLastName() + "'."); + else + sender.sendMessage(ChatColor.RED + "The subgroup '" + auxGroup.getName() + "' is already available to '" + auxUser.getLastName() + "'."); + + return true; + + case manudelsub: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 2) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manudelsub )"); + return true; + } + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + auxGroup = dataHolder.getGroup(args[1]); + if (auxGroup == null) { + sender.sendMessage(ChatColor.RED + "'" + args[1] + "' Group doesnt exist!"); + return true; + } + + // Validating permission + if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getLastName(), senderGroup.getName()) : false)) { + sender.sendMessage(ChatColor.RED + "You can't modify a player with same permissions as you, or higher."); + return true; + } + // Seems OK + auxUser.removeSubGroup(auxGroup); + sender.sendMessage(ChatColor.YELLOW + "You removed subgroup '" + auxGroup.getName() + "' from player '" + auxUser.getLastName() + "' list."); + + // targetPlayer = this.getServer().getPlayer(auxUser.getName()); + // if (targetPlayer != null) + // BukkitPermissions.updatePermissions(targetPlayer); + + return true; + + case mangadd: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 1) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangadd )"); + return true; + } + auxGroup = dataHolder.getGroup(args[0]); + if (auxGroup != null) { + sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group already exists!"); + return true; + } + // Seems OK + auxGroup = dataHolder.createGroup(args[0]); + sender.sendMessage(ChatColor.YELLOW + "You created a group named: " + auxGroup.getName()); + + return true; + + case mangdel: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 1) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangdel )"); + return false; + } + auxGroup = dataHolder.getGroup(args[0]); + if (auxGroup == null) { + sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!"); + return true; + } + // Seems OK + dataHolder.removeGroup(auxGroup.getName()); + sender.sendMessage(ChatColor.YELLOW + "You deleted a group named " + auxGroup.getName() + ", it's users are default group now."); + + BukkitPermissions.updateAllPlayers(); + + return true; + + case manuaddp: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length < 2) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manuaddp [permission2] [permission3]...)"); + return true; + } + + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + + // Validating your permissions + if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getLastName(), senderGroup.getName()) : false)) { + sender.sendMessage(ChatColor.RED + "Can't modify player with same group than you, or higher."); + return true; + } + + for (int i = 1; i < args.length; i++) { + auxString = args[i].replace("'", ""); + + permissionResult = permissionHandler.checkFullUserPermission(senderUser, auxString); + if (!isConsole && !isOpOverride && (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND) || permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION))) { + sender.sendMessage(ChatColor.RED + "You can't add a permission you don't have: '" + auxString + "'"); + continue; + } + // Validating permissions of user + permissionResult = permissionHandler.checkUserOnlyPermission(auxUser, auxString); + if (checkPermissionExists(sender, auxString, permissionResult, "user")) { + continue; + } + // Seems Ok + auxUser.addPermission(auxString); + sender.sendMessage(ChatColor.YELLOW + "You added '" + auxString + "' to player '" + auxUser.getLastName() + "' permissions."); + } + + targetPlayer = this.getServer().getPlayer(auxUser.getLastName()); + if (targetPlayer != null) + BukkitPermissions.updatePermissions(targetPlayer); + + return true; + + case manudelp: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length < 2) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manudelp [permission2] [permission3]...)"); + return true; + } + + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + + for (int i = 1; i < args.length; i++) { + auxString = args[i].replace("'", ""); + + if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getLastName(), senderGroup.getName()) : false)) { + sender.sendMessage(ChatColor.RED + "You can't modify a player with same group as you, or higher."); + continue; + } + // Validating your permissions + permissionResult = permissionHandler.checkFullUserPermission(senderUser, auxString); + if (!isConsole && !isOpOverride && (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND) || permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION))) { + sender.sendMessage(ChatColor.RED + "You can't remove a permission you don't have: '" + auxString + "'"); + continue; + } + // Validating permissions of user + permissionResult = permissionHandler.checkUserOnlyPermission(auxUser, auxString); + if (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND)) { + sender.sendMessage(ChatColor.RED + "The user doesn't have direct access to that permission: '" + auxString + "'"); + continue; + } + if (!auxUser.hasSamePermissionNode(auxString)) { + sender.sendMessage(ChatColor.RED + "This permission node doesn't match any node."); + sender.sendMessage(ChatColor.RED + "But might match node: " + permissionResult.accessLevel); + continue; + } + auxUser.removePermission(auxString); + sender.sendMessage(ChatColor.YELLOW + "You removed '" + auxString + "' from player '" + auxUser.getLastName() + "' permissions."); + } + // Seems OK + + targetPlayer = this.getServer().getPlayer(auxUser.getLastName()); + if (targetPlayer != null) + BukkitPermissions.updatePermissions(targetPlayer); + + return true; + + case manuclearp: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 1) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manuclearp )"); + return true; + } + + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + // Validating your permissions + if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getLastName(), senderGroup.getName()) : false)) { + sender.sendMessage(ChatColor.RED + "You can't modify a player with same group as you, or higher."); + return true; + } + for (final String perm : auxUser.getPermissionList()) { + permissionResult = permissionHandler.checkFullUserPermission(senderUser, perm); + if (!isConsole && !isOpOverride && (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND) || permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION))) { + sender.sendMessage(ChatColor.RED + "You can't remove a permission you don't have: '" + perm + "'."); + } else { + auxUser.removePermission(perm); + } + } + sender.sendMessage(ChatColor.YELLOW + "You removed all permissions from player '" + auxUser.getLastName() + "'."); + + targetPlayer = this.getServer().getPlayer(auxUser.getLastName()); + if (targetPlayer != null) + BukkitPermissions.updatePermissions(targetPlayer); + + return true; + + case manulistp: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if ((args.length == 0) || (args.length > 2)) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manulistp (+))"); + return true; + } + + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + // Validating permission + // Seems OK + auxString = ""; + for (final String perm : auxUser.getPermissionList()) { + auxString += perm + ", "; + } + if (auxString.lastIndexOf(",") > 0) { + auxString = auxString.substring(0, auxString.lastIndexOf(",")); + sender.sendMessage(ChatColor.YELLOW + "The player '" + auxUser.getLastName() + "' has following permissions: " + ChatColor.WHITE + auxString); + sender.sendMessage(ChatColor.YELLOW + "And all permissions from group: " + auxUser.getGroupName()); + auxString = ""; + for (final String subGroup : auxUser.subGroupListStringCopy()) { + auxString += subGroup + ", "; + } + if (auxString.lastIndexOf(",") > 0) { + auxString = auxString.substring(0, auxString.lastIndexOf(",")); + sender.sendMessage(ChatColor.YELLOW + "And all permissions from subgroups: " + auxString); + } + } else { + sender.sendMessage(ChatColor.YELLOW + "The player '" + auxUser.getLastName() + "' has no specific permissions."); + sender.sendMessage(ChatColor.YELLOW + "Only all permissions from group: " + auxUser.getGroupName()); + auxString = ""; + for (final String subGroup : auxUser.subGroupListStringCopy()) { + auxString += subGroup + ", "; + } + if (auxString.lastIndexOf(",") > 0) { + auxString = auxString.substring(0, auxString.lastIndexOf(",")); + sender.sendMessage(ChatColor.YELLOW + "And all permissions from subgroups: " + auxString); + } + } + + // bukkit perms + if ((args.length == 2) && (args[1].equalsIgnoreCase("+"))) { + targetPlayer = this.getServer().getPlayer(auxUser.getLastName()); + if (targetPlayer != null) { + sender.sendMessage(ChatColor.YELLOW + "Superperms reports: "); + for (final String line : BukkitPermissions.listPerms(targetPlayer)) + sender.sendMessage(ChatColor.YELLOW + line); + + } + } + + return true; + + case manucheckp: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 2) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manucheckp )"); + return true; + } + + auxString = args[1].replace("'", ""); + + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + targetPlayer = this.getServer().getPlayer(auxUser.getLastName()); + // Validating permission + permissionResult = permissionHandler.checkFullGMPermission(auxUser, auxString, false); + + if (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND)) { + // No permissions found in GM so fall through and check Bukkit. + sender.sendMessage(ChatColor.YELLOW + "The player doesn't have access to that permission"); + + } else { + // This permission was found in groupmanager. + if (permissionResult.owner instanceof User) { + if (permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION)) { + sender.sendMessage(ChatColor.YELLOW + "The user has directly a negation node for that permission."); + } else if (permissionResult.resultType.equals(PermissionCheckResult.Type.EXCEPTION)) { + sender.sendMessage(ChatColor.YELLOW + "The user has directly an Exception node for that permission."); + } else { + sender.sendMessage(ChatColor.YELLOW + "The user has directly this permission."); + } + sender.sendMessage(ChatColor.YELLOW + "Permission Node: " + permissionResult.accessLevel); + } else if (permissionResult.owner instanceof Group) { + if (permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION)) { + sender.sendMessage(ChatColor.YELLOW + "The user inherits a negation permission from group: " + permissionResult.owner.getLastName()); + } else if (permissionResult.resultType.equals(PermissionCheckResult.Type.EXCEPTION)) { + sender.sendMessage(ChatColor.YELLOW + "The user inherits an Exception permission from group: " + permissionResult.owner.getLastName()); + } else { + sender.sendMessage(ChatColor.YELLOW + "The user inherits the permission from group: " + permissionResult.owner.getLastName()); + } + sender.sendMessage(ChatColor.YELLOW + "Permission Node: " + permissionResult.accessLevel); + } + } + + // superperms + if (targetPlayer != null) { + sender.sendMessage(ChatColor.YELLOW + "SuperPerms reports Node: " + targetPlayer.hasPermission(args[1]) + ((!targetPlayer.hasPermission(args[1]) && targetPlayer.isPermissionSet(args[1])) ? " (Negated)" : "")); + } + + return true; + + case mangaddp: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length < 2) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangaddp [permission2] [permission3]...)"); + return true; + } + + auxGroup = dataHolder.getGroup(args[0]); + if (auxGroup == null) { + sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!"); + return false; + } + + for (int i = 1; i < args.length; i++) { + auxString = args[i].replace("'", ""); + + // Validating your permissions + permissionResult = permissionHandler.checkFullUserPermission(senderUser, auxString); + if (!isConsole && !isOpOverride && (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND) || permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION))) { + sender.sendMessage(ChatColor.RED + "You can't add a permission you don't have: '" + auxString + "'"); + continue; + } + // Validating permissions of user + permissionResult = permissionHandler.checkGroupOnlyPermission(auxGroup, auxString); + if (checkPermissionExists(sender, auxString, permissionResult, "group")) { + continue; + } + // Seems OK + auxGroup.addPermission(auxString); + sender.sendMessage(ChatColor.YELLOW + "You added '" + auxString + "' to group '" + auxGroup.getName() + "' permissions."); + } + + BukkitPermissions.updateAllPlayers(); + + return true; + + case mangdelp: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length < 2) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangdelp [permission2] [permission3]...)"); + return true; + } + + auxGroup = dataHolder.getGroup(args[0]); + if (auxGroup == null) { + sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!"); + return true; + } + for (int i = 1; i < args.length; i++) { + auxString = args[i].replace("'", ""); + + // Validating your permissions + permissionResult = permissionHandler.checkFullUserPermission(senderUser, auxString); + if (!isConsole && !isOpOverride && (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND) || permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION))) { + sender.sendMessage(ChatColor.RED + "Can't remove a permission you don't have: '" + auxString + "'"); + continue; + } + // Validating permissions of user + permissionResult = permissionHandler.checkGroupOnlyPermission(auxGroup, auxString); + if (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND)) { + sender.sendMessage(ChatColor.YELLOW + "The group doesn't have direct access to that permission: '" + auxString + "'"); + continue; + } + if (!auxGroup.hasSamePermissionNode(auxString)) { + sender.sendMessage(ChatColor.RED + "This permission node doesn't match any node."); + sender.sendMessage(ChatColor.RED + "But might match node: " + permissionResult.accessLevel); + continue; + } + // Seems OK + auxGroup.removePermission(auxString); + sender.sendMessage(ChatColor.YELLOW + "You removed '" + auxString + "' from group '" + auxGroup.getName() + "' permissions."); + } + + BukkitPermissions.updateAllPlayers(); + + return true; + + case mangclearp: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 1) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangclearp )"); + return true; + } + + auxGroup = dataHolder.getGroup(args[0]); + if (auxGroup == null) { + sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!"); + return true; + } + + for (final String perm : auxGroup.getPermissionList()) { + permissionResult = permissionHandler.checkFullUserPermission(senderUser, perm); + if (!isConsole && !isOpOverride && (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND) || permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION))) { + sender.sendMessage(ChatColor.RED + "Can't remove a permission you don't have: '" + perm + "'."); + } else { + auxGroup.removePermission(perm); + } + } + sender.sendMessage(ChatColor.YELLOW + "You removed all permissions from group '" + auxGroup.getName() + "'."); + + BukkitPermissions.updateAllPlayers(); + + return true; + + case manglistp: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 1) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manglistp )"); + return true; + } + auxGroup = dataHolder.getGroup(args[0]); + if (auxGroup == null) { + sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!"); + return true; + } + // Validating permission + + // Seems OK + auxString = ""; + for (final String perm : auxGroup.getPermissionList()) { + auxString += perm + ", "; + } + if (auxString.lastIndexOf(",") > 0) { + auxString = auxString.substring(0, auxString.lastIndexOf(",")); + sender.sendMessage(ChatColor.YELLOW + "The group '" + auxGroup.getName() + "' has following permissions: " + ChatColor.WHITE + auxString); + auxString = ""; + for (final String grp : auxGroup.getInherits()) { + auxString += grp + ", "; + } + if (auxString.lastIndexOf(",") > 0) { + auxString = auxString.substring(0, auxString.lastIndexOf(",")); + sender.sendMessage(ChatColor.YELLOW + "And all permissions from groups: " + auxString); + } + + } else { + sender.sendMessage(ChatColor.YELLOW + "The group '" + auxGroup.getName() + "' has no specific permissions."); + auxString = ""; + for (final String grp : auxGroup.getInherits()) { + auxString += grp + ", "; + } + if (auxString.lastIndexOf(",") > 0) { + auxString = auxString.substring(0, auxString.lastIndexOf(",")); + sender.sendMessage(ChatColor.YELLOW + "Only all permissions from groups: " + auxString); + } + + } + return true; + + case mangcheckp: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 2) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangcheckp )"); + return true; + } + + auxString = args[1]; + if (auxString.startsWith("'") && auxString.endsWith("'")) { + auxString = auxString.substring(1, auxString.length() - 1); + } + + auxGroup = dataHolder.getGroup(args[0]); + if (auxGroup == null) { + sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!"); + return true; + } + // Validating permission + permissionResult = permissionHandler.checkGroupPermissionWithInheritance(auxGroup, auxString); + if (permissionResult.resultType.equals(PermissionCheckResult.Type.NOTFOUND)) { + sender.sendMessage(ChatColor.YELLOW + "The group doesn't have access to that permission"); + return true; + } + // Seems OK + // auxString = permissionHandler.checkUserOnlyPermission(auxUser, args[1]); + if (permissionResult.owner instanceof Group) { + if (permissionResult.resultType.equals(PermissionCheckResult.Type.NEGATION)) { + sender.sendMessage(ChatColor.YELLOW + "The group inherits the negation permission from group: " + permissionResult.owner.getLastName()); + } else if (permissionResult.resultType.equals(PermissionCheckResult.Type.EXCEPTION)) { + sender.sendMessage(ChatColor.YELLOW + "The group inherits an Exception permission from group: " + permissionResult.owner.getLastName()); + } else { + sender.sendMessage(ChatColor.YELLOW + "The group inherits the permission from group: " + permissionResult.owner.getLastName()); + } + sender.sendMessage(ChatColor.YELLOW + "Permission Node: " + permissionResult.accessLevel); + + } + return true; + + case mangaddi: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 2) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangaddi )"); + return true; + } + auxGroup = dataHolder.getGroup(args[0]); + if (auxGroup == null) { + sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!"); + return true; + } + auxGroup2 = dataHolder.getGroup(args[1]); + if (auxGroup2 == null) { + sender.sendMessage(ChatColor.RED + "'" + args[1] + "' Group doesnt exist!"); + return true; + } + if (auxGroup.isGlobal()) { + sender.sendMessage(ChatColor.RED + "GlobalGroups do NOT support inheritance."); + return true; + } + + // Validating permission + if (permissionHandler.hasGroupInInheritance(auxGroup, auxGroup2.getName())) { + sender.sendMessage(ChatColor.RED + "Group " + auxGroup.getName() + " already inherits " + auxGroup2.getName() + " (might not be directly)"); + return true; + } + // Seems OK + auxGroup.addInherits(auxGroup2); + sender.sendMessage(ChatColor.RED + "Group " + auxGroup2.getName() + " is now in " + auxGroup.getName() + " inheritance list."); + + BukkitPermissions.updateAllPlayers(); + + return true; + + case mangdeli: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 2) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangdeli )"); + return true; + } + auxGroup = dataHolder.getGroup(args[0]); + if (auxGroup == null) { + sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!"); + return true; + } + auxGroup2 = dataHolder.getGroup(args[1]); + if (auxGroup2 == null) { + sender.sendMessage(ChatColor.RED + "'" + args[1] + "' Group doesnt exist!"); + return true; + } + if (auxGroup.isGlobal()) { + sender.sendMessage(ChatColor.RED + "GlobalGroups do NOT support inheritance."); + return true; + } + + // Validating permission + if (!permissionHandler.hasGroupInInheritance(auxGroup, auxGroup2.getName())) { + sender.sendMessage(ChatColor.RED + "Group " + auxGroup.getName() + " does not inherits " + auxGroup2.getName() + "."); + return true; + } + if (!auxGroup.getInherits().contains(auxGroup2.getName())) { + sender.sendMessage(ChatColor.RED + "Group " + auxGroup.getName() + " does not inherits " + auxGroup2.getName() + " directly."); + return true; + } + // Seems OK + auxGroup.removeInherits(auxGroup2.getName()); + sender.sendMessage(ChatColor.RED + "Group " + auxGroup2.getName() + " was removed from " + auxGroup.getName() + " inheritance list."); + + BukkitPermissions.updateAllPlayers(); + + return true; + + case manuaddv: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length < 3) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manuaddv )"); + return true; + } + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + // Validating permission + // Seems OK + auxString = ""; + for (int i = 2; i < args.length; i++) { + auxString += args[i]; + if ((i + 1) < args.length) { + auxString += " "; + } + } + auxString = auxString.replace("'", ""); + auxUser.getVariables().addVar(args[1], Variables.parseVariableValue(auxString)); + sender.sendMessage(ChatColor.YELLOW + "Variable " + ChatColor.GOLD + args[1] + ChatColor.YELLOW + ":'" + ChatColor.GREEN + auxString + ChatColor.YELLOW + "' added to the user " + auxUser.getLastName()); + + return true; + + case manudelv: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 2) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manudelv )"); + return true; + } + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + // Validating permission + if (!auxUser.getVariables().hasVar(args[1])) { + sender.sendMessage(ChatColor.RED + "The user doesn't have directly that variable!"); + return true; + } + // Seems OK + auxUser.getVariables().removeVar(args[1]); + sender.sendMessage(ChatColor.YELLOW + "Variable " + ChatColor.GOLD + args[1] + ChatColor.YELLOW + " removed from the user " + ChatColor.GREEN + auxUser.getLastName()); + + return true; + + case manulistv: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 1) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manulistv )"); + return true; + } + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + // Validating permission + // Seems OK + auxString = ""; + for (final String varKey : auxUser.getVariables().getVarKeyList()) { + final Object o = auxUser.getVariables().getVarObject(varKey); + auxString += ChatColor.GOLD + varKey + ChatColor.WHITE + ":'" + ChatColor.GREEN + o.toString() + ChatColor.WHITE + "', "; + } + if (auxString.lastIndexOf(",") > 0) { + auxString = auxString.substring(0, auxString.lastIndexOf(",")); + } + sender.sendMessage(ChatColor.YELLOW + "Variables of user " + auxUser.getLastName() + ": "); + sender.sendMessage(auxString + "."); + sender.sendMessage(ChatColor.YELLOW + "Plus all variables from group: " + auxUser.getGroupName()); + + return true; + + case manucheckv: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 2) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manucheckv )"); + return true; + } + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + // Validating permission + auxGroup = auxUser.getGroup(); + auxGroup2 = permissionHandler.nextGroupWithVariable(auxGroup, args[1]); + + if (!auxUser.getVariables().hasVar(args[1])) { + // Check sub groups + if (!auxUser.isSubGroupsEmpty() && auxGroup2 == null) + for (final Group subGroup : auxUser.subGroupListCopy()) { + auxGroup2 = permissionHandler.nextGroupWithVariable(subGroup, args[1]); + if (auxGroup2 != null) + continue; + } + if (auxGroup2 == null) { + sender.sendMessage(ChatColor.YELLOW + "The user doesn't have access to that variable!"); + return true; + } + } + // Seems OK + if (auxUser.getVariables().hasVar(auxString)) { + sender.sendMessage(ChatColor.YELLOW + "The value of variable '" + ChatColor.GOLD + args[1] + ChatColor.YELLOW + "' is: '" + ChatColor.GREEN + auxUser.getVariables().getVarObject(args[1]).toString() + + ChatColor.WHITE + "'"); + sender.sendMessage(ChatColor.YELLOW + "This user own directly the variable"); + } + sender.sendMessage( + ChatColor.YELLOW + "The value of variable '" + ChatColor.GOLD + args[1] + ChatColor.YELLOW + "' is: '" + ChatColor.GREEN + auxGroup2.getVariables().getVarObject(args[1]).toString() + ChatColor.WHITE + "'"); + if (!auxGroup.equals(auxGroup2)) { + sender.sendMessage(ChatColor.YELLOW + "And the value was inherited from group: " + ChatColor.GREEN + auxGroup2.getName()); + } + + return true; + + case mangaddv: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length < 3) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangaddv )"); + return true; + } + auxGroup = dataHolder.getGroup(args[0]); + if (auxGroup == null) { + sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!"); + return true; + } + if (auxGroup.isGlobal()) { + sender.sendMessage(ChatColor.RED + "GlobalGroups do NOT support Info Nodes."); + return true; + } + // Validating permission + // Seems OK + auxString = ""; + for (int i = 2; i < args.length; i++) { + auxString += args[i]; + if ((i + 1) < args.length) { + auxString += " "; + } + } + + auxString = auxString.replace("'", ""); + auxGroup.getVariables().addVar(args[1], Variables.parseVariableValue(auxString)); + sender.sendMessage(ChatColor.YELLOW + "Variable " + ChatColor.GOLD + args[1] + ChatColor.YELLOW + ":'" + ChatColor.GREEN + auxString + ChatColor.YELLOW + "' added to the group " + auxGroup.getName()); + + return true; + + case mangdelv: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 2) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangdelv )"); + return true; + } + auxGroup = dataHolder.getGroup(args[0]); + if (auxGroup == null) { + sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!"); + return true; + } + if (auxGroup.isGlobal()) { + sender.sendMessage(ChatColor.RED + "GlobalGroups do NOT support Info Nodes."); + return true; + } + // Validating permission + if (!auxGroup.getVariables().hasVar(args[1])) { + sender.sendMessage(ChatColor.RED + "The group doesn't have directly that variable!"); + return true; + } + // Seems OK + auxGroup.getVariables().removeVar(args[1]); + sender.sendMessage(ChatColor.YELLOW + "Variable " + ChatColor.GOLD + args[1] + ChatColor.YELLOW + " removed from the group " + ChatColor.GREEN + auxGroup.getName()); + + return true; + + case manglistv: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 1) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manglistv )"); + return true; + } + auxGroup = dataHolder.getGroup(args[0]); + if (auxGroup == null) { + sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!"); + return true; + } + if (auxGroup.isGlobal()) { + sender.sendMessage(ChatColor.RED + "GlobalGroups do NOT support Info Nodes."); + return true; + } + // Validating permission + // Seems OK + auxString = ""; + for (final String varKey : auxGroup.getVariables().getVarKeyList()) { + final Object o = auxGroup.getVariables().getVarObject(varKey); + auxString += ChatColor.GOLD + varKey + ChatColor.WHITE + ":'" + ChatColor.GREEN + o.toString() + ChatColor.WHITE + "', "; + } + if (auxString.lastIndexOf(",") > 0) { + auxString = auxString.substring(0, auxString.lastIndexOf(",")); + } + sender.sendMessage(ChatColor.YELLOW + "Variables of group " + auxGroup.getName() + ": "); + sender.sendMessage(auxString + "."); + auxString = ""; + for (final String grp : auxGroup.getInherits()) { + auxString += grp + ", "; + } + if (auxString.lastIndexOf(",") > 0) { + auxString = auxString.substring(0, auxString.lastIndexOf(",")); + sender.sendMessage(ChatColor.YELLOW + "Plus all variables from groups: " + auxString); + } + + return true; + + case mangcheckv: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 2) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mangcheckv )"); + return true; + } + auxGroup = dataHolder.getGroup(args[0]); + if (auxGroup == null) { + sender.sendMessage(ChatColor.RED + "'" + args[0] + "' Group doesnt exist!"); + return true; + } + if (auxGroup.isGlobal()) { + sender.sendMessage(ChatColor.RED + "GlobalGroups do NOT support Info Nodes."); + return true; + } + // Validating permission + auxGroup2 = permissionHandler.nextGroupWithVariable(auxGroup, args[1]); + if (auxGroup2 == null) { + sender.sendMessage(ChatColor.RED + "The group doesn't have access to that variable!"); + } + // Seems OK + sender.sendMessage( + ChatColor.YELLOW + "The value of variable '" + ChatColor.GOLD + args[1] + ChatColor.YELLOW + "' is: '" + ChatColor.GREEN + auxGroup2.getVariables().getVarObject(args[1]).toString() + ChatColor.WHITE + "'"); + if (!auxGroup.equals(auxGroup2)) { + sender.sendMessage(ChatColor.YELLOW + "And the value was inherited from group: " + ChatColor.GREEN + auxGroup2.getName()); + } + + return true; + + case manwhois: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 1) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manwhois )"); + return true; + } + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + // Seems OK + sender.sendMessage(ChatColor.YELLOW + "Name: " + ChatColor.GREEN + auxUser.getLastName()); + sender.sendMessage(ChatColor.YELLOW + "Group: " + ChatColor.GREEN + auxUser.getGroup().getName()); + // Compile a list of subgroups + auxString = ""; + for (final String subGroup : auxUser.subGroupListStringCopy()) { + auxString += subGroup + ", "; + } + if (auxString.lastIndexOf(",") > 0) { + auxString = auxString.substring(0, auxString.lastIndexOf(",")); + sender.sendMessage(ChatColor.YELLOW + "subgroups: " + auxString); + } + + sender.sendMessage(ChatColor.YELLOW + "Overloaded: " + ChatColor.GREEN + dataHolder.isOverloaded(auxUser.getLastName())); + auxGroup = dataHolder.surpassOverload(auxUser.getLastName()).getGroup(); + if (!auxGroup.equals(auxUser.getGroup())) { + sender.sendMessage(ChatColor.YELLOW + "Original Group: " + ChatColor.GREEN + auxGroup.getName()); + } + // victim.permissions.add(args[1]); + return true; + + case tempadd: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 1) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/tempadd )"); + return true; + } + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + // Validating permission + if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getLastName(), senderGroup.getName()) : false)) { + sender.sendMessage(ChatColor.RED + "Can't modify player with same permissions than you, or higher."); + return true; + } + // Seems OK + if (overloadedUsers.get(dataHolder.getName().toLowerCase()) == null) { + overloadedUsers.put(dataHolder.getName().toLowerCase(), new ArrayList()); + } + dataHolder.overloadUser(auxUser.getLastName()); + overloadedUsers.get(dataHolder.getName().toLowerCase()).add(dataHolder.getUser(auxUser.getLastName())); + sender.sendMessage(ChatColor.YELLOW + "Player set to overload mode!"); + + return true; + + case tempdel: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 1) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/tempdel )"); + return true; + } + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + // Validating permission + if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getLastName(), senderGroup.getName()) : false)) { + sender.sendMessage(ChatColor.RED + "You can't modify a player with same permissions as you, or higher."); + return true; + } + // Seems OK + if (overloadedUsers.get(dataHolder.getName().toLowerCase()) == null) { + overloadedUsers.put(dataHolder.getName().toLowerCase(), new ArrayList()); + } + dataHolder.removeOverload(auxUser.getLastName()); + if (overloadedUsers.get(dataHolder.getName().toLowerCase()).contains(auxUser)) { + overloadedUsers.get(dataHolder.getName().toLowerCase()).remove(auxUser); + } + sender.sendMessage(ChatColor.YELLOW + "Player overload mode is now disabled."); + + return true; + + case templist: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // WORKING + auxString = ""; + removeList = new ArrayList(); + count = 0; + for (final User u : overloadedUsers.get(dataHolder.getName().toLowerCase())) { + if (!dataHolder.isOverloaded(u.getLastName())) { + removeList.add(u); + } else { + auxString += u.getLastName() + ", "; + count++; + } + } + if (count == 0) { + sender.sendMessage(ChatColor.YELLOW + "There are no users in overload mode."); + return true; + } + auxString = auxString.substring(0, auxString.lastIndexOf(",")); + if (overloadedUsers.get(dataHolder.getName().toLowerCase()) == null) { + overloadedUsers.put(dataHolder.getName().toLowerCase(), new ArrayList()); + } + overloadedUsers.get(dataHolder.getName().toLowerCase()).removeAll(removeList); + sender.sendMessage(ChatColor.YELLOW + " " + count + " Users in overload mode: " + ChatColor.WHITE + auxString); + + return true; + + case tempdelall: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // WORKING + removeList = new ArrayList(); + count = 0; + for (final User u : overloadedUsers.get(dataHolder.getName().toLowerCase())) { + if (dataHolder.isOverloaded(u.getLastName())) { + dataHolder.removeOverload(u.getLastName()); + count++; + } + } + if (count == 0) { + sender.sendMessage(ChatColor.YELLOW + "There are no users in overload mode."); + return true; + } + if (overloadedUsers.get(dataHolder.getName().toLowerCase()) == null) { + overloadedUsers.put(dataHolder.getName().toLowerCase(), new ArrayList()); + } + overloadedUsers.get(dataHolder.getName().toLowerCase()).clear(); + sender.sendMessage(ChatColor.YELLOW + " " + count + "All users in overload mode are now normal again."); + + return true; + + case mansave: + + boolean forced = false; + + if ((args.length == 1) && (args[0].equalsIgnoreCase("force"))) + forced = true; + + try { + worldsHolder.saveChanges(forced); + sender.sendMessage(ChatColor.YELLOW + "All changes were saved."); + } catch (final IllegalStateException ex) { + sender.sendMessage(ChatColor.RED + ex.getMessage()); + } + return true; + + case manload: + + /** + * Attempt to reload a specific world + */ + if (args.length > 0) { + + if (!lastError.isEmpty()) { + sender.sendMessage(ChatColor.RED + "All commands are locked due to an error. " + ChatColor.BOLD + "" + ChatColor.UNDERLINE + "Check plugins/groupmanager/error.log or console" + ChatColor.RESET + "" + + ChatColor.RED + " and then try a '/manload'."); + return true; + } + + auxString = ""; + for (int i = 0; i < args.length; i++) { + auxString += args[i]; + if ((i + 1) < args.length) { + auxString += " "; + } + } + + isLoaded = false; // Disable Bukkit Perms update and event triggers + + globalGroups.load(); + worldsHolder.loadWorld(auxString); + + sender.sendMessage("The request to reload world '" + auxString + "' was attempted."); + + isLoaded = true; + + BukkitPermissions.reset(); + + } else { + + /** + * Reload all settings and data as no world was specified. + */ + + /* + * Attempting a fresh load. + */ + onDisable(true); + onEnable(true); + + sender.sendMessage("All settings and worlds were reloaded!"); + } + + /** + * Fire an event as none will have been triggered in the reload. + */ + if (GroupManager.isLoaded()) + GroupManager.getGMEventHandler().callEvent(GMSystemEvent.Action.RELOADED); + + return true; + + case listgroups: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // WORKING + auxString = ""; + String auxString2 = ""; + for (final Group g : dataHolder.getGroupList()) { + auxString += g.getName() + ", "; + } + for (final Group g : getGlobalGroups().getGroupList()) { + auxString2 += g.getName() + ", "; + } + if (auxString.lastIndexOf(",") > 0) { + auxString = auxString.substring(0, auxString.lastIndexOf(",")); + } + if (auxString2.lastIndexOf(",") > 0) { + auxString2 = auxString2.substring(0, auxString2.lastIndexOf(",")); + } + sender.sendMessage(ChatColor.YELLOW + "Groups Available: " + ChatColor.WHITE + auxString); + sender.sendMessage(ChatColor.YELLOW + "GlobalGroups Available: " + ChatColor.WHITE + auxString2); + + return true; + + case manpromote: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 2) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manpromote )"); + return true; + } + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + auxGroup = dataHolder.getGroup(args[1]); + if (auxGroup == null) { + sender.sendMessage(ChatColor.RED + "'" + args[1] + "' Group doesnt exist!"); + return true; + } + if (auxGroup.isGlobal()) { + sender.sendMessage(ChatColor.RED + "Players may not be members of GlobalGroups directly."); + return true; + } + // Validating permission + if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getLastName(), senderGroup.getName()) : false)) { + sender.sendMessage(ChatColor.RED + "You can't modify a player with same permissions as you, or higher."); + return true; + } + if (!isConsole && !isOpOverride && (permissionHandler.hasGroupInInheritance(auxGroup, senderGroup.getName()))) { + sender.sendMessage(ChatColor.RED + "The destination group can't be the same as yours, or higher."); + return true; + } + if (!isConsole && !isOpOverride && (!permissionHandler.inGroup(senderUser.getLastName(), auxUser.getGroupName()) || !permissionHandler.inGroup(senderUser.getLastName(), auxGroup.getName()))) { + sender.sendMessage(ChatColor.RED + "You can't modify a player involving a group that you don't inherit."); + return true; + } + if (!permissionHandler.hasGroupInInheritance(auxUser.getGroup(), auxGroup.getName()) && !permissionHandler.hasGroupInInheritance(auxGroup, auxUser.getGroupName())) { + sender.sendMessage(ChatColor.RED + "You can't modify a player using groups with different heritage line."); + return true; + } + if (!permissionHandler.hasGroupInInheritance(auxGroup, auxUser.getGroupName())) { + sender.sendMessage(ChatColor.RED + "The new group must be a higher rank."); + return true; + } + // Seems OK + auxUser.setGroup(auxGroup); + if (!sender.hasPermission("groupmanager.notify.other") || (isConsole)) + sender.sendMessage(ChatColor.YELLOW + "You changed " + auxUser.getLastName() + " group to " + auxGroup.getName() + "."); + + return true; + + case mandemote: + // Validating state of sender + if (dataHolder == null || permissionHandler == null) { + if (!setDefaultWorldHandler(sender)) + return true; + } + // Validating arguments + if (args.length != 2) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mandemote )"); + return true; + } + if ((validateOnlinePlayer) && ((match = validatePlayer(args[0], sender)) == null)) { + return false; + } + if (match != null) { + auxUser = dataHolder.getUser(match.get(0)); + } else { + auxUser = dataHolder.getUser(args[0]); + } + auxGroup = dataHolder.getGroup(args[1]); + if (auxGroup == null) { + sender.sendMessage(ChatColor.RED + "'" + args[1] + "' Group doesnt exist!"); + return true; + } + if (auxGroup.isGlobal()) { + sender.sendMessage(ChatColor.RED + "Players may not be members of GlobalGroups directly."); + return true; + } + // Validating permission + if (!isConsole && !isOpOverride && (senderGroup != null ? permissionHandler.inGroup(auxUser.getLastName(), senderGroup.getName()) : false)) { + sender.sendMessage(ChatColor.RED + "You can't modify a player with same permissions as you, or higher."); + return true; + } + if (!isConsole && !isOpOverride && (permissionHandler.hasGroupInInheritance(auxGroup, senderGroup.getName()))) { + sender.sendMessage(ChatColor.RED + "The destination group can't be the same as yours, or higher."); + return true; + } + if (!isConsole && !isOpOverride && (!permissionHandler.inGroup(senderUser.getLastName(), auxUser.getGroupName()) || !permissionHandler.inGroup(senderUser.getLastName(), auxGroup.getName()))) { + sender.sendMessage(ChatColor.RED + "You can't modify a player involving a group that you don't inherit."); + return true; + } + if (!permissionHandler.hasGroupInInheritance(auxUser.getGroup(), auxGroup.getName()) && !permissionHandler.hasGroupInInheritance(auxGroup, auxUser.getGroupName())) { + sender.sendMessage(ChatColor.RED + "You can't modify a player using groups with different inheritage line."); + return true; + } + if (permissionHandler.hasGroupInInheritance(auxGroup, auxUser.getGroupName())) { + sender.sendMessage(ChatColor.RED + "The new group must be a lower rank."); + return true; + } + // Seems OK + auxUser.setGroup(auxGroup); + if (!sender.hasPermission("groupmanager.notify.other") || (isConsole)) + sender.sendMessage(ChatColor.YELLOW + "You changed " + auxUser.getLastName() + " group to " + auxGroup.getName() + "."); + + return true; + + case mantogglevalidate: + validateOnlinePlayer = !validateOnlinePlayer; + sender.sendMessage(ChatColor.YELLOW + "Validate if player is online, now set to: " + Boolean.toString(validateOnlinePlayer)); + if (!validateOnlinePlayer) { + sender.sendMessage(ChatColor.GOLD + "From now on you can edit players that are not connected... BUT:"); + sender.sendMessage(ChatColor.LIGHT_PURPLE + "From now on you should type the whole name of the player, correctly."); + } + return true; + case mantogglesave: + if (scheduler == null) { + enableScheduler(); + sender.sendMessage(ChatColor.YELLOW + "The auto-saving is enabled!"); + } else { + disableScheduler(); + sender.sendMessage(ChatColor.YELLOW + "The auto-saving is disabled!"); + } + return true; + case manworld: + auxString = selectedWorlds.get(sender.getName()); + if (auxString != null) { + sender.sendMessage(ChatColor.YELLOW + "You have the world '" + dataHolder.getName() + "' in your selection."); + } else { + if (dataHolder == null) { + sender.sendMessage(ChatColor.YELLOW + "There is no world selected. And no world is available now."); + } else { + sender.sendMessage(ChatColor.YELLOW + "You don't have a world in your selection.."); + sender.sendMessage(ChatColor.YELLOW + "Working with the direct world where your player is."); + sender.sendMessage(ChatColor.YELLOW + "Your world now uses permissions of world name: '" + dataHolder.getName() + "' "); + } + } + + return true; + + case manselect: + if (args.length < 1) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/manselect )"); + sender.sendMessage(ChatColor.YELLOW + "Worlds available: "); + final ArrayList worlds = worldsHolder.allWorldsDataList(); + auxString = ""; + for (int i = 0; i < worlds.size(); i++) { + auxString += worlds.get(i).getName(); + if ((i + 1) < worlds.size()) { + auxString += ", "; + } + } + sender.sendMessage(ChatColor.YELLOW + auxString); + return false; + } + auxString = ""; + for (int i = 0; i < args.length; i++) { + if (args[i] == null) { + logger.warning("Bukkit gave invalid arguments array! Cmd: " + cmd.getName() + " args.length: " + args.length); + return false; + } + auxString += args[i]; + if (i < (args.length - 1)) { + auxString += " "; + } + } + dataHolder = worldsHolder.getWorldData(auxString); + permissionHandler = dataHolder.getPermissionsHandler(); + selectedWorlds.put(sender.getName(), dataHolder.getName()); + sender.sendMessage(ChatColor.YELLOW + "You have selected world '" + dataHolder.getName() + "'."); + + return true; + + case manclear: + if (args.length != 0) { + sender.sendMessage(ChatColor.RED + "Review your arguments count!"); + return false; + } + selectedWorlds.remove(sender.getName()); + sender.sendMessage(ChatColor.YELLOW + "You have removed your world selection. Working with current world(if possible)."); + + return true; + + case mancheckw: + if (args.length < 1) { + sender.sendMessage(ChatColor.RED + "Review your arguments count! (/mancheckw )"); + sender.sendMessage(ChatColor.YELLOW + "Worlds available: "); + final ArrayList worlds = worldsHolder.allWorldsDataList(); + auxString = ""; + for (int i = 0; i < worlds.size(); i++) { + auxString += worlds.get(i).getName(); + if ((i + 1) < worlds.size()) { + auxString += ", "; + } + } + sender.sendMessage(ChatColor.YELLOW + auxString); + return false; + } + + auxString = ""; + for (int i = 0; i < args.length; i++) { + if (args[i] == null) { + logger.warning("Bukkit gave invalid arguments array! Cmd: " + cmd.getName() + " args.length: " + args.length); + return false; + } + auxString += args[i]; + if (i < (args.length - 1)) { + auxString += " "; + } + } + dataHolder = worldsHolder.getWorldData(auxString); + + sender.sendMessage(ChatColor.YELLOW + "You have selected world '" + dataHolder.getName() + "'."); + sender.sendMessage(ChatColor.YELLOW + "This world is using the following data files.."); + sender.sendMessage(ChatColor.YELLOW + "Groups:" + ChatColor.GREEN + " " + dataHolder.getGroupsFile().getAbsolutePath()); + sender.sendMessage(ChatColor.YELLOW + "Users:" + ChatColor.GREEN + " " + dataHolder.getUsersFile().getAbsolutePath()); + + return true; + + default: + break; + } + } + sender.sendMessage(ChatColor.RED + "You are not allowed to use that command."); + return true; + } + + @Override + public void onDisable() { + + onDisable(false); + } + + public void onDisable(final boolean restarting) { + + setLoaded(false); + + if (!restarting) { + // Unregister this service if we are shutting down. + this.getServer().getServicesManager().unregister(this.worldsHolder); + } + + disableScheduler(); // Shutdown before we save, so it doesn't interfere. + if (worldsHolder != null) { + try { + worldsHolder.saveChanges(false); + } catch (final IllegalStateException ex) { + GroupManager.logger.log(Level.WARNING, ex.getMessage()); + } + } + + // Remove all attachments before clearing + if (BukkitPermissions != null) { + BukkitPermissions.removeAllAttachments(); + } + + if (!restarting) { + + if (WorldEvents != null) + WorldEvents = null; + + BukkitPermissions = null; + + } + + // EXAMPLE: Custom code, here we just output some info so we can check that all is well + final PluginDescriptionFile pdfFile = this.getDescription(); + System.out.println(pdfFile.getName() + " version " + pdfFile.getVersion() + " is disabled!"); + + if (!restarting) + GroupManager.logger.removeHandler(ch); + } + + @Override + public void onEnable() { + /* + * Initialize the event handler + */ + setGMEventHandler(new GroupManagerEventHandler(this)); + onEnable(false); + new VersionChecker(this); + } + + public void onEnable(final boolean restarting) { + + try { + /* + * reset local variables. + */ + overloadedUsers = new HashMap>(); + selectedWorlds = new HashMap(); + lastError = ""; + + /* + * Setup our logger if we are not restarting. + */ + if (!restarting) { + GroupManager.logger.setUseParentHandlers(false); + ch = new GMLoggerHandler(); + GroupManager.logger.addHandler(ch); + } + GroupManager.logger.setLevel(Level.ALL); + + // Create the backup folder, if it doesn't exist. + prepareFileFields(); + // Load the config.yml + prepareConfig(); + // Load the global groups + globalGroups = new GlobalGroups(this); + + /* + * Configure the worlds holder. + */ + if (!restarting) + worldsHolder = new WorldsHolder(this); + else + worldsHolder.resetWorldsHolder(); + + /* + * This should NEVER happen. No idea why it's still here. + */ + final PluginDescriptionFile pdfFile = this.getDescription(); + if (worldsHolder == null) { + GroupManager.logger.severe("Can't enable " + pdfFile.getName() + " version " + pdfFile.getVersion() + ", bad loading!"); + this.getServer().getPluginManager().disablePlugin(this); + throw new IllegalStateException("An error ocurred while loading GroupManager"); + } + + /* + * Prevent our registered events from triggering + * updates as we are not fully loaded. + */ + setLoaded(false); + + /* + * Initialize the world listener and bukkit permissions + * to handle events if this is a fresh start + * + * else + * + * Reset bukkit perms. + */ + if (!restarting) { + WorldEvents = new GMWorldListener(this); + BukkitPermissions = new BukkitPermissions(this); + } else { + BukkitPermissions.reset(); + } + + /* + * Start the scheduler for data saving. + */ + enableScheduler(); + + /* + * Schedule a Bukkit Permissions update for 1 tick later. + * All plugins will be loaded by then + */ + + if (getServer().getScheduler().scheduleSyncDelayedTask(this, new BukkitPermsUpdateTask(), 1) == -1) { + GroupManager.logger.severe("Could not schedule superperms Update."); + /* + * Flag that we are now loaded and should start processing events. + */ + setLoaded(true); + } + + System.out.println(pdfFile.getName() + " version " + pdfFile.getVersion() + " is enabled!"); + + // Register as a service + if (!restarting) + this.getServer().getServicesManager().register(WorldsHolder.class, this.worldsHolder, this, ServicePriority.Lowest); + + } catch (final Exception ex) { + + /* + * Store the error and write to the log. + */ + saveErrorLog(ex); + + /* + * Throw an error so Bukkit knows about it. + */ + throw new IllegalArgumentException(ex.getMessage(), ex); + + } + } + + /** + * @param validateOnlinePlayer + * the validateOnlinePlayer to set + */ + public void setValidateOnlinePlayer(final boolean validateOnlinePlayer) { + + this.validateOnlinePlayer = validateOnlinePlayer; + } + + /** + * Checks if a permission exists and of a lower or same priority. + */ + private boolean checkPermissionExists(final CommandSender sender, final String newPerm, final PermissionCheckResult oldPerm, final String type) { + + if (newPerm.startsWith("+")) { + if (oldPerm.resultType.equals(PermissionCheckResult.Type.EXCEPTION)) { + sender.sendMessage(ChatColor.RED + "The " + type + " already has direct access to that permission."); + sender.sendMessage(ChatColor.RED + "Node: " + oldPerm.accessLevel); + return true; + } + } else if (newPerm.startsWith("-")) { + if (oldPerm.resultType.equals(PermissionCheckResult.Type.EXCEPTION)) { + sender.sendMessage(ChatColor.RED + "The " + type + " already has an exception for this node."); + sender.sendMessage(ChatColor.RED + "Node: " + oldPerm.accessLevel); + return true; + } else if (oldPerm.resultType.equals(PermissionCheckResult.Type.NEGATION)) { + sender.sendMessage(ChatColor.RED + "The " + type + " already has a matching negated node."); + sender.sendMessage(ChatColor.RED + "Node: " + oldPerm.accessLevel); + return true; + } + } else { + if (oldPerm.resultType.equals(PermissionCheckResult.Type.EXCEPTION)) { + sender.sendMessage(ChatColor.RED + "The " + type + " already has an exception for this node."); + sender.sendMessage(ChatColor.RED + "Node: " + oldPerm.accessLevel); + } else if (oldPerm.resultType.equals(PermissionCheckResult.Type.NEGATION)) { + sender.sendMessage(ChatColor.RED + "The " + type + " already has a matching negated node."); + sender.sendMessage(ChatColor.RED + "Node: " + oldPerm.accessLevel); + } else if (oldPerm.resultType.equals(PermissionCheckResult.Type.FOUND)) { + sender.sendMessage(ChatColor.RED + "The " + type + " already has direct access to that permission."); + sender.sendMessage(ChatColor.RED + "Node: " + oldPerm.accessLevel); + + // Since not all plugins define wildcard permissions, allow setting the permission anyway if the permissions dont match exactly. + return (oldPerm.accessLevel.equalsIgnoreCase(newPerm)); + } + } + return false; + } + + private void prepareConfig() { + + config = new GMConfiguration(this); + } + + private void prepareFileFields() { + + backupFolder = new File(this.getDataFolder(), "backup"); + if (!backupFolder.exists()) { + getBackupFolder().mkdirs(); + } + } + + /** + * Write an error.log + * + * @param ex + */ + private void saveErrorLog(final Exception ex) { + + if (!getDataFolder().exists()) { + getDataFolder().mkdirs(); + } + + lastError = ex.getMessage(); + + GroupManager.logger.severe("==================================================="); + GroupManager.logger.severe("= ERROR REPORT START - " + this.getDescription().getVersion() + " ="); + GroupManager.logger.severe("==================================================="); + GroupManager.logger.severe("=== PLEASE COPY AND PASTE THE ERROR.LOG FROM THE =="); + GroupManager.logger.severe("= GROUPMANAGER FOLDER TO AN ESSENTIALS DEVELOPER ="); + GroupManager.logger.severe("==================================================="); + GroupManager.logger.severe(lastError); + GroupManager.logger.severe("==================================================="); + GroupManager.logger.severe("= ERROR REPORT ENDED ="); + GroupManager.logger.severe("==================================================="); + + // Append this error to the error log. + try { + String error = "=============================== GM ERROR LOG ===============================\n"; + error += "= ERROR REPORT START - " + this.getDescription().getVersion() + " =\n\n"; + + error += Tasks.getStackTraceAsString(ex); + error += "\n============================================================================\n"; + + Tasks.appendStringToFile(error, (getDataFolder() + System.getProperty("file.separator") + "ERROR.LOG")); + } catch (final IOException e) { + // Failed to write file. + e.printStackTrace(); + } + + } + + /** + * Sets up the default world for use. + */ + private boolean setDefaultWorldHandler(final CommandSender sender) { + + dataHolder = worldsHolder.getWorldData(worldsHolder.getDefaultWorld().getName()); + permissionHandler = dataHolder.getPermissionsHandler(); + + if ((dataHolder != null) && (permissionHandler != null)) { + selectedWorlds.put(sender.getName(), dataHolder.getName()); + sender.sendMessage(ChatColor.RED + "Couldn't retrieve your world. Default world '" + worldsHolder.getDefaultWorld().getName() + "' selected."); + return true; + } + + sender.sendMessage(ChatColor.RED + "Couldn't retrieve your world. World selection is needed."); + sender.sendMessage(ChatColor.RED + "Use /manselect "); + return false; + + } + + /** + * Load a List of players matching the name given. If none online, check + * Offline. + * + * @param playerName, + * sender + * @return true if a single match is found + */ + private List validatePlayer(final String playerName, final CommandSender sender) { + + List players = new ArrayList(); + final List match = new ArrayList(); + + players = this.getServer().matchPlayer(playerName); + if (players.isEmpty()) { + // Check for an offline player (exact match). + if (Arrays.asList(this.getServer().getOfflinePlayers()).contains(Bukkit.getOfflinePlayer(playerName))) { + match.add(playerName); + } else { + // look for partial matches + for (final OfflinePlayer offline : this.getServer().getOfflinePlayers()) { + if (offline.getName().toLowerCase().startsWith(playerName.toLowerCase())) + match.add(offline.getName()); + } + } + + } else { + for (final Player player : players) { + match.add(player.getName()); + } + } + + if (match.isEmpty() || match == null) { + sender.sendMessage(ChatColor.RED + "Player not found!"); + return null; + } else if (match.size() > 1) { + sender.sendMessage(ChatColor.RED + "Too many matches found! (" + match.toString() + ")"); + return null; + } + + return match; + + } +} diff --git a/src/main/java/org/anjocaido/groupmanager/Tasks/BukkitPermsUpdateTask.java b/src/main/java/org/anjocaido/groupmanager/Tasks/BukkitPermsUpdateTask.java new file mode 100644 index 0000000..6db4b20 --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/Tasks/BukkitPermsUpdateTask.java @@ -0,0 +1,29 @@ +package org.anjocaido.groupmanager.Tasks; + +import org.anjocaido.groupmanager.GroupManager; + +/* + * + * Created by ElgarL + */ + +public class BukkitPermsUpdateTask implements Runnable { + + public BukkitPermsUpdateTask() { + + super(); + } + + @Override + public void run() { + + // Signal loaded and update BukkitPermissions. + GroupManager.setLoaded(true); + GroupManager.BukkitPermissions.collectPermissions(); + GroupManager.BukkitPermissions.updateAllPlayers(); + + GroupManager.logger.info("Bukkit Permissions Updated!"); + + } + +} \ No newline at end of file diff --git a/src/main/java/org/anjocaido/groupmanager/data/DataUnit.java b/src/main/java/org/anjocaido/groupmanager/data/DataUnit.java new file mode 100644 index 0000000..2dc9150 --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/data/DataUnit.java @@ -0,0 +1,205 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.anjocaido.groupmanager.data; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.anjocaido.groupmanager.GroupManager; +import org.anjocaido.groupmanager.dataholder.WorldDataHolder; +import org.anjocaido.groupmanager.utils.StringPermissionComparator; + +/** + * + * @author gabrielcouto + */ +public abstract class DataUnit { + + private WorldDataHolder dataSource; + private String uUID; + private String lastName; + private boolean changed, sorted = false; + private List permissions = Collections.unmodifiableList(Collections. emptyList()); + + public DataUnit(WorldDataHolder dataSource, String name) { + + this.dataSource = dataSource; + this.uUID = name; + } + + public DataUnit(String name) { + + this.uUID = name; + } + + /** + * Every group is matched only by their names and DataSources names. + * + * @param o + * @return true if they are equal. false if not. + */ + @Override + public boolean equals(Object o) { + + if (o instanceof DataUnit) { + DataUnit go = (DataUnit) o; + if (this.getUUID().equalsIgnoreCase(go.getUUID())) { + // Global Group match. + if (this.dataSource == null && go.getDataSource() == null) + return true; + // This is a global group, the object to test isn't. + if (this.dataSource == null && go.getDataSource() != null) + return false; + // This is not a global group, but the object to test is. + if (this.dataSource != null && go.getDataSource() == null) + return false; + // Match on group name and world name. + if (this.dataSource.getName().equalsIgnoreCase(go.getDataSource().getName())) + return true; + } + } + return false; + } + + @Override + public int hashCode() { + + int hash = 5; + hash = 71 * hash + (this.uUID != null ? this.uUID.toLowerCase().hashCode() : 0); + return hash; + } + + /** + * Set the data source to point to a different worldDataHolder + * + * @param source + */ + public void setDataSource(WorldDataHolder source) { + + this.dataSource = source; + } + + /** + * Get the current worldDataHolder this object is pointing to + * + * @return the dataSource + */ + public WorldDataHolder getDataSource() { + + return dataSource; + } + + public String getUUID() { + + return uUID; + } + + public String getLastName() { + + if (uUID.length() < 36) + return this.uUID; + + return this.lastName; + } + + public void setLastName(String lastName) { + + if (!lastName.equals(this.lastName)) { + + this.lastName = lastName; + changed = true; + + } + + } + + public void flagAsChanged() { + + WorldDataHolder testSource = getDataSource(); + String source = ""; + + if (testSource == null) + source = "GlobalGroups"; + else + source = testSource.getName(); + + GroupManager.logger.finest("DataSource: " + source + " - DataUnit: " + getUUID() + " flagged as changed!"); + // for(StackTraceElement st: Thread.currentThread().getStackTrace()){ + // GroupManager.logger.finest(st.toString()); + // } + sorted = false; + changed = true; + } + + public boolean isChanged() { + + return changed; + } + + public void flagAsSaved() { + + WorldDataHolder testSource = getDataSource(); + String source = ""; + + if (testSource == null) + source = "GlobalGroups"; + else + source = testSource.getName(); + + GroupManager.logger.finest("DataSource: " + source + " - DataUnit: " + getUUID() + " flagged as saved!"); + changed = false; + } + + public boolean hasSamePermissionNode(String permission) { + + return permissions.contains(permission); + } + + public void addPermission(String permission) { + + if (!hasSamePermissionNode(permission)) { + List clone = new ArrayList(permissions); + clone.add(permission); + permissions = Collections.unmodifiableList(clone); + } + flagAsChanged(); + } + + public boolean removePermission(String permission) { + + flagAsChanged(); + List clone = new ArrayList(permissions); + boolean ret = clone.remove(permission); + permissions = Collections.unmodifiableList(clone); + return ret; + } + + /** + * Use this only to list permissions. + * You can't edit the permissions using the returned ArrayList instance + * + * @return a copy of the permission list + */ + public List getPermissionList() { + sortPermissions(); + return permissions; + } + + public boolean isSorted() { + + return this.sorted; + } + + public void sortPermissions() { + + if (!isSorted()) { + List clone = new ArrayList(permissions); + Collections.sort(clone, StringPermissionComparator.getInstance()); + permissions = Collections.unmodifiableList(clone); + sorted = true; + } + } +} \ No newline at end of file diff --git a/src/main/java/org/anjocaido/groupmanager/data/Group.java b/src/main/java/org/anjocaido/groupmanager/data/Group.java new file mode 100644 index 0000000..45431fa --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/data/Group.java @@ -0,0 +1,197 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.anjocaido.groupmanager.data; + +import org.anjocaido.groupmanager.GroupManager; +import org.anjocaido.groupmanager.dataholder.WorldDataHolder; +import org.anjocaido.groupmanager.events.GMGroupEvent.Action; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * + * @author gabrielcouto/ElgarL + */ +public class Group extends DataUnit implements Cloneable { + + /** + * The group it inherits DIRECTLY! + */ + private List inherits = Collections.unmodifiableList(Collections. emptyList()); + /** + * This one holds the fields in INFO node. + * like prefix = 'c' + * or build = false + */ + private GroupVariables variables = new GroupVariables(this); + + /** + * Constructor for individual World Groups. + * + * @param name + */ + public Group(WorldDataHolder source, String name) { + + super(source, name); + } + + /** + * Constructor for Global Groups. + * + * @param name + */ + public Group(String name) { + + super(name); + } + + /** + * @return the name + */ + public String getName() { + + return this.getUUID(); + } + + /** + * Is this a GlobalGroup + * + * @return true if this is a global group + */ + public boolean isGlobal() { + + return (getDataSource() == null); + } + + /** + * Clone this group + * + * @return a clone of this group + */ + @Override + public Group clone() { + + Group clone; + + if (isGlobal()) { + clone = new Group(this.getName()); + } else { + clone = new Group(getDataSource(), this.getName()); + clone.inherits = this.getInherits().isEmpty() ? Collections.unmodifiableList(Collections. emptyList()) : Collections.unmodifiableList(new ArrayList(this.getInherits())); + } + + for (String perm : this.getPermissionList()) { + clone.addPermission(perm); + } + clone.variables = ((GroupVariables) variables).clone(clone); + // clone.flagAsChanged(); + return clone; + } + + /** + * Use this to deliver a group from a different dataSource to another + * + * @param dataSource + * @return Null or Clone + */ + public Group clone(WorldDataHolder dataSource) { + + if (dataSource.groupExists(this.getName())) { + return null; + } + + Group clone = dataSource.createGroup(this.getName()); + + // Don't add inheritance for GlobalGroups + if (!isGlobal()) { + clone.inherits = this.getInherits().isEmpty() ? Collections.unmodifiableList(Collections. emptyList()) : Collections.unmodifiableList(new ArrayList(this.getInherits())); + } + for (String perm : this.getPermissionList()) { + clone.addPermission(perm); + } + clone.variables = variables.clone(clone); + clone.flagAsChanged(); // use this to make the new dataSource save the new group + return clone; + } + + /** + * an unmodifiable list of inherits list + * You can't manage the list by here + * Lol... version 0.6 had a problem because this. + * + * @return the inherits + */ + public List getInherits() { + return inherits; + } + + /** + * @param inherit + * the inherits to set + */ + public void addInherits(Group inherit) { + + if (!isGlobal()) { + if (!this.getDataSource().groupExists(inherit.getName())) { + getDataSource().addGroup(inherit); + } + if (!inherits.contains(inherit.getName().toLowerCase())) { + List clone = new ArrayList(inherits); + clone.add(inherit.getName().toLowerCase()); + inherits = Collections.unmodifiableList(clone); + } + flagAsChanged(); + if (GroupManager.isLoaded()) { + GroupManager.BukkitPermissions.updateAllPlayers(); + GroupManager.getGMEventHandler().callEvent(this, Action.GROUP_INHERITANCE_CHANGED); + } + } + } + + public boolean removeInherits(String inherit) { + + if (!isGlobal()) { + if (this.inherits.contains(inherit.toLowerCase())) { + List clone = new ArrayList(inherits); + clone.remove(inherit.toLowerCase()); + inherits = Collections.unmodifiableList(clone); + flagAsChanged(); + GroupManager.getGMEventHandler().callEvent(this, Action.GROUP_INHERITANCE_CHANGED); + return true; + } + } + return false; + } + + /** + * @return the variables + */ + public GroupVariables getVariables() { + + return variables; + } + + /** + * + * @param varList + */ + public void setVariables(Map varList) { + + if (!isGlobal()) { + GroupVariables temp = new GroupVariables(this, varList); + variables.clearVars(); + for (String key : temp.getVarKeyList()) { + variables.addVar(key, temp.getVarObject(key)); + } + flagAsChanged(); + if (GroupManager.isLoaded()) { + GroupManager.BukkitPermissions.updateAllPlayers(); + GroupManager.getGMEventHandler().callEvent(this, Action.GROUP_INFO_CHANGED); + } + } + } +} diff --git a/src/main/java/org/anjocaido/groupmanager/data/GroupVariables.java b/src/main/java/org/anjocaido/groupmanager/data/GroupVariables.java new file mode 100644 index 0000000..c1dec99 --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/data/GroupVariables.java @@ -0,0 +1,97 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.anjocaido.groupmanager.data; + +import java.util.Map; + +/** + * + * @author gabrielcouto + */ +public class GroupVariables extends Variables implements Cloneable { + + private Group owner; + + public GroupVariables(Group owner) { + + super(owner); + this.owner = owner; + addVar("prefix", ""); + addVar("suffix", ""); + addVar("build", false); + } + + public GroupVariables(Group owner, Map varList) { + + super(owner); + variables.clear(); + variables.putAll(varList); + if (variables.get("prefix") == null) { + variables.put("prefix", ""); + owner.flagAsChanged(); + } + // thisGrp.prefix = infoNode.get("prefix").toString(); + + if (variables.get("suffix") == null) { + variables.put("suffix", ""); + owner.flagAsChanged(); + } + // thisGrp.suffix = infoNode.get("suffix").toString(); + + if (variables.get("build") == null) { + variables.put("build", false); + owner.flagAsChanged(); + } + this.owner = owner; + } + + /** + * A clone of all vars here. + * + * @return GroupVariables clone + */ + protected GroupVariables clone(Group newOwner) { + + GroupVariables clone = new GroupVariables(newOwner); + synchronized (variables) { + for (String key : variables.keySet()) { + clone.variables.put(key, variables.get(key)); + } + } + newOwner.flagAsChanged(); + return clone; + } + + /** + * Remove a var from the list + * + * @param name + */ + @Override + public void removeVar(String name) { + + try { + this.variables.remove(name); + } catch (Exception e) { + } + if (name.equals("prefix")) { + addVar("prefix", ""); + } else if (name.equals("suffix")) { + addVar("suffix", ""); + } else if (name.equals("build")) { + addVar("build", false); + } + owner.flagAsChanged(); + } + + /** + * @return the owner + */ + @Override + public Group getOwner() { + + return owner; + } +} diff --git a/src/main/java/org/anjocaido/groupmanager/data/User.java b/src/main/java/org/anjocaido/groupmanager/data/User.java new file mode 100644 index 0000000..c971ee6 --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/data/User.java @@ -0,0 +1,323 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.anjocaido.groupmanager.data; + +// import com.sun.org.apache.bcel.internal.generic.AALOAD; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.anjocaido.groupmanager.GroupManager; +import org.anjocaido.groupmanager.dataholder.WorldDataHolder; +import org.anjocaido.groupmanager.events.GMUserEvent.Action; +import java.util.Map; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +/** + * + * @author gabrielcouto/ElgarL + */ +public class User extends DataUnit implements Cloneable { + + /** + * + */ + private String group = null; + private final List subGroups = Collections.synchronizedList(new ArrayList()); + /** + * This one holds the fields in INFO node. like prefix = 'c' or build = + * false + */ + private UserVariables variables = new UserVariables(this); + private transient Player bukkitPlayer = null; + + /** + * + * @param name + */ + public User(WorldDataHolder source, String name) { + + super(source, name); + this.group = source.getDefaultGroup().getName(); + } + + /** + * + * @return User clone + */ + @Override + public User clone() { + + User clone = new User(getDataSource(), this.getLastName()); + clone.group = this.group; + + // Clone all subgroups. + clone.subGroups.addAll(this.subGroupListStringCopy()); + + for (String perm : this.getPermissionList()) { + clone.addPermission(perm); + } + // clone.variables = this.variables.clone(); + // clone.flagAsChanged(); + return clone; + } + + /** + * Use this to deliver a user from one WorldDataHolder to another + * + * @param dataSource + * @return null if given dataSource already contains the same user + */ + public User clone(WorldDataHolder dataSource) { + + if (dataSource.isUserDeclared(this.getUUID())) { + return null; + } + + User clone = dataSource.createUser(this.getUUID()); + + if (dataSource.getGroup(group) == null) { + clone.setGroup(dataSource.getDefaultGroup()); + } else { + clone.setGroup(dataSource.getGroup(this.getGroupName())); + } + + // Clone all subgroups. + clone.subGroups.addAll(this.subGroupListStringCopy()); + + for (String perm : this.getPermissionList()) { + clone.addPermission(perm); + } + + clone.variables = this.variables.clone(this); + clone.flagAsChanged(); + return clone; + } + + public User clone(String uUID, String CurrentName) { + + User clone = this.getDataSource().createUser(uUID); + + clone.setLastName(CurrentName); + + // Set the group silently. + clone.setGroup(this.getDataSource().getGroup(this.getGroupName()), false); + + // Clone all subgroups. + clone.subGroups.addAll(this.subGroupListStringCopy()); + + for (String perm : this.getPermissionList()) { + clone.addPermission(perm); + } + + clone.variables = this.variables.clone(this); + clone.flagAsChanged(); + + return clone; + } + + public Group getGroup() { + + Group result = getDataSource().getGroup(group); + if (result == null) { + this.setGroup(getDataSource().getDefaultGroup()); + result = getDataSource().getDefaultGroup(); + } + return result; + } + + /** + * @return the group + */ + public String getGroupName() { + + Group result = getDataSource().getGroup(group); + if (result == null) { + group = getDataSource().getDefaultGroup().getName(); + } + return group; + } + + /** + * Place holder to let people know to stop using this method. + * + * @deprecated use {@link #getLastName()} and {@link #getUUID()}. + * @return a string containing the players last known name. + */ + @Deprecated + public String getName() { + + return this.getLastName(); + + } + + /** + * @param group + * the group to set + */ + public void setGroup(Group group) { + + setGroup(group, true); + } + + /** + * @param group + * the group to set + * @param updatePerms + * if we are to trigger a superperms update. + * + */ + public void setGroup(Group group, Boolean updatePerms) { + + if (!this.getDataSource().groupExists(group.getName())) { + getDataSource().addGroup(group); + } + group = getDataSource().getGroup(group.getName()); + String oldGroup = this.group; + this.group = group.getName(); + flagAsChanged(); + if (GroupManager.isLoaded()) { + if (!GroupManager.BukkitPermissions.isPlayer_join() && (updatePerms)) + GroupManager.BukkitPermissions.updatePlayer(getBukkitPlayer()); + + // Do we notify of the group change? + String defaultGroupName = getDataSource().getDefaultGroup().getName(); + // if we were not in the default group + // or we were in the default group and the move is to a different + // group. + boolean notify = (!oldGroup.equalsIgnoreCase(defaultGroupName)) || ((oldGroup.equalsIgnoreCase(defaultGroupName)) && (!this.group.equalsIgnoreCase(defaultGroupName))); + + if (notify) + GroupManager.notify(this.getLastName(), String.format(" moved to the group %s in %s.", group.getName(), this.getDataSource().getName())); + + if (updatePerms) + GroupManager.getGMEventHandler().callEvent(this, Action.USER_GROUP_CHANGED); + } + } + + public boolean addSubGroup(Group subGroup) { + + // Don't allow adding a subgroup if it's already set as the primary. + if (this.group.equalsIgnoreCase(subGroup.getName())) { + return false; + } + // User already has this subgroup + if (containsSubGroup(subGroup)) + return false; + + // If the group doesn't exists add it + if (!this.getDataSource().groupExists(subGroup.getName())) { + getDataSource().addGroup(subGroup); + } + + subGroups.add(subGroup.getName()); + flagAsChanged(); + if (GroupManager.isLoaded()) { + if (!GroupManager.BukkitPermissions.isPlayer_join()) + GroupManager.BukkitPermissions.updatePlayer(getBukkitPlayer()); + GroupManager.getGMEventHandler().callEvent(this, Action.USER_SUBGROUP_CHANGED); + } + return true; + + // subGroup = getDataSource().getGroup(subGroup.getName()); + // removeSubGroup(subGroup); + // subGroups.add(subGroup.getName()); + } + + public int subGroupsSize() { + + return subGroups.size(); + } + + public boolean isSubGroupsEmpty() { + + return subGroups.isEmpty(); + } + + public boolean containsSubGroup(Group subGroup) { + + return subGroups.contains(subGroup.getName()); + } + + public boolean removeSubGroup(Group subGroup) { + + try { + if (subGroups.remove(subGroup.getName())) { + flagAsChanged(); + if (GroupManager.isLoaded()) + if (!GroupManager.BukkitPermissions.isPlayer_join()) + GroupManager.BukkitPermissions.updatePlayer(getBukkitPlayer()); + GroupManager.getGMEventHandler().callEvent(this, Action.USER_SUBGROUP_CHANGED); + return true; + } + } catch (Exception e) { + } + return false; + } + + public ArrayList subGroupListCopy() { + + ArrayList val = new ArrayList(); + synchronized (subGroups) { + for (String gstr : subGroups) { + Group g = getDataSource().getGroup(gstr); + if (g == null) { + removeSubGroup(g); + continue; + } + val.add(g); + } + } + return val; + } + + public ArrayList subGroupListStringCopy() { + synchronized (subGroups) { + return new ArrayList(subGroups); + } + } + + /** + * @return the variables + */ + public UserVariables getVariables() { + + return variables; + } + + /** + * + * @param varList + */ + public void setVariables(Map varList) { + + // UserVariables temp = new UserVariables(this, varList); + variables.clearVars(); + for (String key : varList.keySet()) { + variables.addVar(key, varList.get(key)); + } + flagAsChanged(); + if (GroupManager.isLoaded()) { + // if (!GroupManager.BukkitPermissions.isPlayer_join()) + // GroupManager.BukkitPermissions.updatePlayer(this.getName()); + GroupManager.getGMEventHandler().callEvent(this, Action.USER_INFO_CHANGED); + } + } + + public User updatePlayer(Player player) { + + bukkitPlayer = player; + return this; + } + + public Player getBukkitPlayer() { + + if (bukkitPlayer == null) { + bukkitPlayer = Bukkit.getPlayer(this.getLastName()); + } + return bukkitPlayer; + } +} diff --git a/src/main/java/org/anjocaido/groupmanager/data/UserVariables.java b/src/main/java/org/anjocaido/groupmanager/data/UserVariables.java new file mode 100644 index 0000000..acab10e --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/data/UserVariables.java @@ -0,0 +1,56 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.anjocaido.groupmanager.data; + +import java.util.Map; + +/** + * + * @author gabrielcouto + */ +public class UserVariables extends Variables { + + private User owner; + + public UserVariables(User owner) { + + super(owner); + this.owner = owner; + } + + public UserVariables(User owner, Map varList) { + + super(owner); + this.variables.clear(); + this.variables.putAll(varList); + this.owner = owner; + } + + /** + * A clone of all vars here. + * + * @return UserVariables clone + */ + protected UserVariables clone(User newOwner) { + + UserVariables clone = new UserVariables(newOwner); + synchronized (variables) { + for (String key : variables.keySet()) { + clone.variables.put(key, variables.get(key)); + } + } + newOwner.flagAsChanged(); + return clone; + } + + /** + * @return the owner + */ + @Override + public User getOwner() { + + return owner; + } +} diff --git a/src/main/java/org/anjocaido/groupmanager/data/Variables.java b/src/main/java/org/anjocaido/groupmanager/data/Variables.java new file mode 100644 index 0000000..48953a3 --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/data/Variables.java @@ -0,0 +1,213 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.anjocaido.groupmanager.data; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * A class that holds variables of a user/group. + * In groups, it holds the contents of INFO node. + * Like: + * prefix + * suffix + * build + * + * @author gabrielcouto + */ +public abstract class Variables implements Cloneable { + + private DataUnit owner; + protected final Map variables = Collections.synchronizedMap(new HashMap()); + + public Variables(DataUnit owner) { + + this.owner = owner; + } + + /** + * Add var to the the INFO node. + * examples: + * addVar("build",true); + * addVar("prefix","c"); + * + * @param name + * key name of the var + * @param o + * the object value of the var + */ + public void addVar(String name, Object o) { + + if (o == null) { + return; + } + if (variables.containsKey(name)) { + variables.remove(name); + } + variables.put(name, o); + owner.flagAsChanged(); + } + + /** + * Returns the object inside the var + * + * @param name + * @return a Object if exists. null if doesn't exists + */ + public Object getVarObject(String name) { + + return variables.get(name); + } + + /** + * Get the String value for the given var name + * + * @param name + * the var key name + * @return "" if null. or the toString() value of object + */ + public String getVarString(String name) { + + Object o = variables.get(name); + try { + return o == null ? "" : o.toString(); + } catch (Exception e) { + return ""; + } + } + + /** + * + * @param name + * @return false if null. or a Boolean.parseBoolean of the string + */ + public Boolean getVarBoolean(String name) { + + Object o = variables.get(name); + try { + return o == null ? false : Boolean.parseBoolean(o.toString()); + } catch (Exception e) { + return false; + } + } + + /** + * + * @param name + * @return -1 if null. or a parseInt of the string + */ + public Integer getVarInteger(String name) { + + Object o = variables.get(name); + try { + return o == null ? -1 : Integer.parseInt(o.toString()); + } catch (Exception e) { + return -1; + } + } + + /** + * + * @param name + * @return -1 if null. or a parseDouble of the string + */ + public Double getVarDouble(String name) { + + Object o = variables.get(name); + try { + return o == null ? -1.0D : Double.parseDouble(o.toString()); + } catch (Exception e) { + return -1.0D; + } + } + + /** + * All variable keys this is holding + * + * @return Set of all variable names. + */ + public String[] getVarKeyList() { + synchronized (variables) { + return variables.keySet().toArray(new String[0]); + } + } + + /** + * verify is a var exists + * + * @param name + * the key name of the var + * @return true if that var exists + */ + public boolean hasVar(String name) { + + return variables.containsKey(name); + } + + /** + * Returns the quantity of vars this is holding + * + * @return the number of vars + */ + public int getSize() { + + return variables.size(); + } + + /** + * Remove a var from the list + * + * @param name + */ + public void removeVar(String name) { + + try { + variables.remove(name); + } catch (Exception e) { + } + owner.flagAsChanged(); + } + + public static Object parseVariableValue(String value) { + + try { + Integer i = Integer.parseInt(value); + return i; + } catch (NumberFormatException e) { + } + try { + Double d = Double.parseDouble(value); + return d; + } catch (NumberFormatException e) { + } + if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes") || value.equalsIgnoreCase("on")) { + return true; + } else if (value.equalsIgnoreCase("false") || value.equalsIgnoreCase("no") || value.equalsIgnoreCase("off")) { + return false; + } + return value; + + } + + public void clearVars() { + + variables.clear(); + owner.flagAsChanged(); + } + + /** + * @return the owner + */ + public DataUnit getOwner() { + + return owner; + } + + public boolean isEmpty() { + + return variables.isEmpty(); + } +} diff --git a/src/main/java/org/anjocaido/groupmanager/dataholder/GroupsDataHolder.java b/src/main/java/org/anjocaido/groupmanager/dataholder/GroupsDataHolder.java new file mode 100644 index 0000000..9fc6b97 --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/dataholder/GroupsDataHolder.java @@ -0,0 +1,136 @@ +package org.anjocaido.groupmanager.dataholder; + +import java.io.File; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.anjocaido.groupmanager.data.Group; + +/** + * This container holds all Groups loaded from the relevant groupsFile. + * + * @author ElgarL + * + */ +public class GroupsDataHolder { + + private WorldDataHolder dataSource; + private Group defaultGroup = null; + private File groupsFile; + private boolean haveGroupsChanged = false; + private long timeStampGroups = 0; + + /** + * The actual groups holder + */ + private final Map groups = Collections.synchronizedMap(new HashMap()); + + /** + * Constructor + */ + protected GroupsDataHolder() { + + } + + public void setDataSource(WorldDataHolder dataSource) { + + this.dataSource = dataSource; + // push this data source to the users, so they pull the correct groups data. + synchronized (groups) { + for (Group group : groups.values()) + group.setDataSource(this.dataSource); + } + } + + public WorldDataHolder getDataSource() { + + return this.dataSource; + } + + /** + * @return the defaultGroup + */ + public Group getDefaultGroup() { + + return defaultGroup; + } + + /** + * @param defaultGroup + * the defaultGroup to set + */ + public void setDefaultGroup(Group defaultGroup) { + + this.defaultGroup = defaultGroup; + } + + /** + * Note: Iteration over this object has to be synchronized! + * + * @return the groups + */ + public Map getGroups() { + + return groups; + } + + /** + * Resets the Groups + */ + public void resetGroups() { + this.groups.clear(); + } + + /** + * @return the groupsFile + */ + public File getGroupsFile() { + + return groupsFile; + } + + /** + * @param groupsFile + * the groupsFile to set + */ + public void setGroupsFile(File groupsFile) { + + this.groupsFile = groupsFile; + } + + /** + * @return the haveGroupsChanged + */ + public boolean HaveGroupsChanged() { + + return haveGroupsChanged; + } + + /** + * @param haveGroupsChanged + * the haveGroupsChanged to set + */ + public void setGroupsChanged(boolean haveGroupsChanged) { + + this.haveGroupsChanged = haveGroupsChanged; + } + + /** + * @return the timeStampGroups + */ + public long getTimeStampGroups() { + + return timeStampGroups; + } + + /** + * @param timeStampGroups + * the timeStampGroups to set + */ + public void setTimeStampGroups(long timeStampGroups) { + + this.timeStampGroups = timeStampGroups; + } + +} diff --git a/src/main/java/org/anjocaido/groupmanager/dataholder/OverloadedWorldHolder.java b/src/main/java/org/anjocaido/groupmanager/dataholder/OverloadedWorldHolder.java new file mode 100644 index 0000000..59c8496 --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/dataholder/OverloadedWorldHolder.java @@ -0,0 +1,220 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.anjocaido.groupmanager.dataholder; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.anjocaido.groupmanager.data.User; + +/** + * + * @author gabrielcouto + */ +public class OverloadedWorldHolder extends WorldDataHolder { + + /** + * + */ + protected final Map overloadedUsers = Collections.synchronizedMap(new HashMap()); + + /** + * + * @param ph + */ + public OverloadedWorldHolder(WorldDataHolder ph) { + + super(ph.getName()); + this.setGroupsFile(ph.getGroupsFile()); + this.setUsersFile(ph.getUsersFile()); + this.groups = ph.groups; + this.users = ph.users; + } + + /** + * + * @param userName + * @return user object or a new user if none exists. + */ + @Override + public User getUser(String userName) { + + // OVERLOADED CODE + String userNameLowered = userName.toLowerCase(); + if (overloadedUsers.containsKey(userNameLowered)) { + return overloadedUsers.get(userNameLowered); + } + // END CODE + + return super.getUser(userName); + } + + /** + * + * @param theUser + */ + @Override + public void addUser(User theUser) { + + if (theUser.getDataSource() != this) { + theUser = theUser.clone(this); + } + if (theUser == null) { + return; + } + if ((theUser.getGroup() == null) || (!getGroups().containsKey(theUser.getGroupName().toLowerCase()))) { + theUser.setGroup(getDefaultGroup()); + } + // OVERLOADED CODE + if (overloadedUsers.containsKey(theUser.getUUID().toLowerCase())) { + overloadedUsers.remove(theUser.getUUID().toLowerCase()); + overloadedUsers.put(theUser.getUUID().toLowerCase(), theUser); + return; + } + // END CODE + removeUser(theUser.getUUID()); + getUsers().put(theUser.getUUID().toLowerCase(), theUser); + setUsersChanged(true); + } + + /** + * + * @param userId + * @return true if removed/false if not found. + */ + @Override + public boolean removeUser(String userId) { + + // OVERLOADED CODE + if (overloadedUsers.containsKey(userId.toLowerCase())) { + overloadedUsers.remove(userId.toLowerCase()); + return true; + } + // END CODE + if (getUsers().containsKey(userId.toLowerCase())) { + getUsers().remove(userId.toLowerCase()); + setUsersChanged(true); + return true; + } + return false; + } + + @Override + public boolean removeGroup(String groupName) { + + if (groupName.equals(getDefaultGroup())) { + return false; + } + synchronized (getGroups()) { + for (String key : getGroups().keySet()) { + if (groupName.equalsIgnoreCase(key)) { + getGroups().remove(key); + synchronized (getUsers()) { + for (String userKey : getUsers().keySet()) { + User user = getUsers().get(userKey); + if (user.getGroupName().equalsIgnoreCase(key)) { + user.setGroup(getDefaultGroup()); + } + + } + } + // OVERLOADED CODE + synchronized (overloadedUsers) { + for (String userKey : overloadedUsers.keySet()) { + User user = overloadedUsers.get(userKey); + if (user.getGroupName().equalsIgnoreCase(key)) { + user.setGroup(getDefaultGroup()); + } + + } + } + // END OVERLOAD + setGroupsChanged(true); + return true; + } + } + } + return false; + } + + /** + * + * @return Collection of all users + */ + @Override + public Collection getUserList() { + + Collection overloadedList = new ArrayList(); + synchronized (getUsers()) { + Collection normalList = getUsers().values(); + for (User u : normalList) { + if (overloadedUsers.containsKey(u.getUUID().toLowerCase())) { + overloadedList.add(overloadedUsers.get(u.getUUID().toLowerCase())); + } else { + overloadedList.add(u); + } + } + } + return overloadedList; + } + + /** + * + * @param userId + * @return true if user is overloaded. + */ + public boolean isOverloaded(String userId) { + + return overloadedUsers.containsKey(userId.toLowerCase()); + } + + /** + * + * @param userId + */ + public void overloadUser(String userId) { + + if (!isOverloaded(userId)) { + User theUser = getUser(userId); + theUser = theUser.clone(); + if (overloadedUsers.containsKey(theUser.getUUID().toLowerCase())) { + overloadedUsers.remove(theUser.getUUID().toLowerCase()); + } + overloadedUsers.put(theUser.getUUID().toLowerCase(), theUser); + } + } + + /** + * + * @param userId + */ + public void removeOverload(String userId) { + + User theUser = getUser(userId); + overloadedUsers.remove(theUser.getUUID().toLowerCase()); + } + + /** + * Gets the user in normal state. Surpassing the overload state. + * It doesn't affect permissions. But it enables plugins change the + * actual user permissions even in overload mode. + * + * @param userId + * @return user object + */ + public User surpassOverload(String userId) { + + if (!isOverloaded(userId)) { + return getUser(userId); + } + if (getUsers().containsKey(userId.toLowerCase())) { + return getUsers().get(userId.toLowerCase()); + } + User newUser = createUser(userId); + return newUser; + } +} \ No newline at end of file diff --git a/src/main/java/org/anjocaido/groupmanager/dataholder/UsersDataHolder.java b/src/main/java/org/anjocaido/groupmanager/dataholder/UsersDataHolder.java new file mode 100644 index 0000000..255709b --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/dataholder/UsersDataHolder.java @@ -0,0 +1,118 @@ +package org.anjocaido.groupmanager.dataholder; + +import java.io.File; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.anjocaido.groupmanager.data.User; + +/** + * This container holds all Users loaded from the relevant usersFile. + * + * @author ElgarL + * + */ +public class UsersDataHolder { + + private WorldDataHolder dataSource; + private File usersFile; + private boolean haveUsersChanged = false; + private long timeStampUsers = 0; + + /** + * The actual groups holder + */ + private final Map users = Collections.synchronizedMap(new HashMap()); + + /** + * Constructor + */ + protected UsersDataHolder() { + + } + + public void setDataSource(WorldDataHolder dataSource) { + + this.dataSource = dataSource; + // push this data source to the users, so they pull the correct groups data. + synchronized (users) { + for (User user : users.values()) + user.setDataSource(this.dataSource); + } + } + + /** + * Note: Iteration over this object has to be synchronized! + * + * @return the users + */ + public Map getUsers() { + + return users; + } + + public WorldDataHolder getDataSource() { + + return this.dataSource; + } + + /** + * Resets the Users + */ + public void resetUsers() { + this.users.clear(); + } + + /** + * @return the usersFile + */ + public File getUsersFile() { + + return usersFile; + } + + /** + * @param usersFile + * the usersFile to set + */ + public void setUsersFile(File usersFile) { + + this.usersFile = usersFile; + } + + /** + * @return the haveUsersChanged + */ + public boolean HaveUsersChanged() { + + return haveUsersChanged; + } + + /** + * @param haveUsersChanged + * the haveUsersChanged to set + */ + public void setUsersChanged(boolean haveUsersChanged) { + + this.haveUsersChanged = haveUsersChanged; + } + + /** + * @return the timeStampUsers + */ + public long getTimeStampUsers() { + + return timeStampUsers; + } + + /** + * @param timeStampUsers + * the timeStampUsers to set + */ + public void setTimeStampUsers(long timeStampUsers) { + + this.timeStampUsers = timeStampUsers; + } + +} diff --git a/src/main/java/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java b/src/main/java/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java new file mode 100644 index 0000000..c7dc0f2 --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/dataholder/WorldDataHolder.java @@ -0,0 +1,1466 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.anjocaido.groupmanager.dataholder; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.anjocaido.groupmanager.GroupManager; +import org.anjocaido.groupmanager.data.Group; +import org.anjocaido.groupmanager.data.User; +import org.anjocaido.groupmanager.events.GMGroupEvent; +import org.anjocaido.groupmanager.events.GMSystemEvent; +import org.anjocaido.groupmanager.events.GMUserEvent; +import org.anjocaido.groupmanager.events.GMUserEvent.Action; +import org.anjocaido.groupmanager.permissions.AnjoPermissionsHandler; +import org.bukkit.Server; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.reader.UnicodeReader; + +/** + * One instance of this should exist per world/mirror it contains all functions + * to manage these data sets and points to the relevant users and groups + * objects. + * + * @author gabrielcouto, ElgarL + */ +public class WorldDataHolder { + + /** + * World name + */ + protected String name; + /** + * The actual groups holder + */ + protected GroupsDataHolder groups = new GroupsDataHolder(); + /** + * The actual users holder + */ + protected UsersDataHolder users = new UsersDataHolder(); + /** + * + */ + protected AnjoPermissionsHandler permissionsHandler; + + /** + * Prevent direct instantiation + * + * @param worldName + */ + public WorldDataHolder(String worldName) { + + name = worldName; + } + + /** + * The main constructor for a new WorldDataHolder + * + * @param worldName + * @param groups + * @param users + */ + public WorldDataHolder(String worldName, GroupsDataHolder groups, UsersDataHolder users) { + + this.name = worldName; + this.groups = groups; + this.users = users; + + // this.defaultGroup = defaultGroup; + } + + /** + * update the dataSource to point to this object. + * + * This should be called whenever a set of world data is fetched. + */ + public void updateDataSource() { + + this.groups.setDataSource(this); + this.users.setDataSource(this); + } + + /** + * Search for a user. If it doesn't exist, create a new one with default + * group. + * + * @param userId + * the UUID String or name of the user + * @return class that manage that user permission + */ + public User getUser(String userId) { + + if (getUsers().containsKey(userId.toLowerCase())) { + return getUsers().get(userId.toLowerCase()); + } + + // Legacy name matching + if (userId.length() < 36) { + + // Search for a LastName match + for (User user : getUserList()) { + + if (user.getLastName().equalsIgnoreCase(userId)) { + return user; + } + } + + } + + // No user account found so create a new one. + User newUser = createUser(userId); + + return newUser; + } + + /** + * *** Internal GM use only *** + * This is called when a player joins to update/add their UUID. + * + * @param uUID + * the player objects UUID. + * @param currentName + * the name they have just logged in with. + * @return the user object for this player. + */ + public User getUser(String uUID, String currentName) { + + // Check for a UUID account + User user = getUsers().get(uUID.toLowerCase()); + + if (user != null) { + + user.setLastName(currentName); + return user; + + } + + // Search for a LastName match + for (User usr : getUserList()) { + + if (usr.getLastName().equalsIgnoreCase(currentName) && usr.getUUID().equalsIgnoreCase(usr.getLastName())) { + + // Clone this user so we can set it's uUID + user = usr.clone(uUID, currentName); + + // Delete it and replace with the new clone. + this.removeUser(usr.getUUID()); + this.addUser(user); + + return getUsers().get(uUID.toLowerCase()); + } + + } + + // No user account found so create a new one. + User newUser = createUser(uUID); + newUser.setLastName(currentName); + + return newUser; + } + + /** + * Add a user to the list. If it already exists, overwrite the old. + * + * @param theUser + * the user you want to add to the permission list + */ + public void addUser(User theUser) { + + if (theUser.getDataSource() != this) { + theUser = theUser.clone(this); + } + if (theUser == null) { + return; + } + if ((theUser.getGroup() == null)) { + theUser.setGroup(groups.getDefaultGroup()); + } + removeUser(theUser.getUUID()); + getUsers().put(theUser.getUUID().toLowerCase(), theUser); + setUsersChanged(true); + if (GroupManager.isLoaded()) + GroupManager.getGMEventHandler().callEvent(theUser, Action.USER_ADDED); + } + + /** + * Removes the user from the list. (he might become a default user) + * + * @param userId + * the UUID or username for the user to remove + * @return true if it had something to remove + */ + public boolean removeUser(String userId) { + + if (getUsers().containsKey(userId.toLowerCase())) { + getUsers().remove(userId.toLowerCase()); + setUsersChanged(true); + if (GroupManager.isLoaded()) + GroupManager.getGMEventHandler().callEvent(userId, GMUserEvent.Action.USER_REMOVED); + return true; + } + return false; + } + + /** + * + * @param userId + * @return true if we have data for this player. + */ + public boolean isUserDeclared(String userId) { + + return getUsers().containsKey(userId.toLowerCase()); + } + + /** + * Change the default group of the file. + * + * @param group + * the group you want make default. + */ + public void setDefaultGroup(Group group) { + + if (!getGroups().containsKey(group.getName().toLowerCase()) || (group.getDataSource() != this)) { + addGroup(group); + } + groups.setDefaultGroup(getGroup(group.getName())); + setGroupsChanged(true); + if (GroupManager.isLoaded()) + GroupManager.getGMEventHandler().callEvent(GMSystemEvent.Action.DEFAULT_GROUP_CHANGED); + } + + /** + * Returns the default group of the file + * + * @return the default group + */ + public Group getDefaultGroup() { + + return groups.getDefaultGroup(); + } + + /** + * Returns a group of the given name + * + * @param groupName + * the name of the group + * @return a group if it is found. null if not found. + */ + public Group getGroup(String groupName) { + + if (groupName.toLowerCase().startsWith("g:")) + return GroupManager.getGlobalGroups().getGroup(groupName); + else + return getGroups().get(groupName.toLowerCase()); + } + + /** + * Check if a group exists. Its the same of getGroup, but check if it is + * null. + * + * @param groupName + * the name of the group + * @return true if exists. false if not. + */ + public boolean groupExists(String groupName) { + + if (groupName.toLowerCase().startsWith("g:")) + return GroupManager.getGlobalGroups().hasGroup(groupName); + else + return getGroups().containsKey(groupName.toLowerCase()); + } + + /** + * Add a group to the list + * + * @param groupToAdd + */ + public void addGroup(Group groupToAdd) { + + if (groupToAdd.getName().toLowerCase().startsWith("g:")) { + GroupManager.getGlobalGroups().addGroup(groupToAdd); + GroupManager.getGMEventHandler().callEvent(groupToAdd, GMGroupEvent.Action.GROUP_ADDED); + return; + } + + if (groupToAdd.getDataSource() != this) { + groupToAdd = groupToAdd.clone(this); + } + removeGroup(groupToAdd.getName()); + getGroups().put(groupToAdd.getName().toLowerCase(), groupToAdd); + setGroupsChanged(true); + if (GroupManager.isLoaded()) + GroupManager.getGMEventHandler().callEvent(groupToAdd, GMGroupEvent.Action.GROUP_ADDED); + } + + /** + * Remove the group from the list + * + * @param groupName + * @return true if had something to remove. false the group was default or + * non-existant + */ + public boolean removeGroup(String groupName) { + + if (groupName.toLowerCase().startsWith("g:")) { + return GroupManager.getGlobalGroups().removeGroup(groupName); + } + + if (getDefaultGroup() != null && groupName.equalsIgnoreCase(getDefaultGroup().getName())) { + return false; + } + if (getGroups().containsKey(groupName.toLowerCase())) { + getGroups().remove(groupName.toLowerCase()); + setGroupsChanged(true); + if (GroupManager.isLoaded()) + GroupManager.getGMEventHandler().callEvent(groupName.toLowerCase(), GMGroupEvent.Action.GROUP_REMOVED); + return true; + } + return false; + + } + + /** + * Creates a new User with the given name and adds it to this holder. + * + * @param userId + * the UUID or username you want + * @return null if user already exists. or new User + */ + public User createUser(String userId) { + + if (getUsers().containsKey(userId.toLowerCase())) { + return null; + } + User newUser = new User(this, userId); + newUser.setGroup(groups.getDefaultGroup(), false); + addUser(newUser); + setUsersChanged(true); + return newUser; + } + + /** + * Creates a new Group with the given name and adds it to this holder + * + * @param groupName + * the groupname you want + * @return null if group already exists. or new Group + */ + public Group createGroup(String groupName) { + + if (groupName.toLowerCase().startsWith("g:")) { + Group newGroup = new Group(groupName); + return GroupManager.getGlobalGroups().newGroup(newGroup); + } + + if (getGroups().containsKey(groupName.toLowerCase())) { + return null; + } + + Group newGroup = new Group(this, groupName); + addGroup(newGroup); + setGroupsChanged(true); + return newGroup; + } + + /** + * + * @return a collection of the groups + */ + public Collection getGroupList() { + + synchronized (getGroups()) { + return new ArrayList(getGroups().values()); + } + } + + /** + * + * @return a collection of the users + */ + public Collection getUserList() { + + synchronized (getUsers()) { + return new ArrayList(getUsers().values()); + } + } + + /** + * reads the file again + */ + public void reload() { + + try { + reloadGroups(); + reloadUsers(); + } catch (Exception ex) { + Logger.getLogger(WorldDataHolder.class.getName()).log(Level.SEVERE, null, ex); + } + } + + /** + * Refresh Group data from file + */ + public void reloadGroups() { + + GroupManager.setLoaded(false); + try { + // temporary holder in case the load fails. + WorldDataHolder ph = new WorldDataHolder(this.getName()); + + loadGroups(ph, getGroupsFile()); + // transfer new data + resetGroups(); + for (Group tempGroup : ph.getGroupList()) { + tempGroup.clone(this); + } + this.setDefaultGroup(getGroup(ph.getDefaultGroup().getName())); + this.removeGroupsChangedFlag(); + this.setTimeStampGroups(getGroupsFile().lastModified()); + + ph = null; + } catch (Exception ex) { + Logger.getLogger(WorldDataHolder.class.getName()).log(Level.WARNING, null, ex); + } + GroupManager.setLoaded(true); + GroupManager.getGMEventHandler().callEvent(GMSystemEvent.Action.RELOADED); + } + + /** + * Refresh Users data from file + */ + public void reloadUsers() { + + GroupManager.setLoaded(false); + try { + // temporary holder in case the load fails. + WorldDataHolder ph = new WorldDataHolder(this.getName()); + // copy groups for reference + for (Group tempGroup : this.getGroupList()) { + tempGroup.clone(ph); + } + // setup the default group before loading user data. + ph.setDefaultGroup(ph.getGroup(getDefaultGroup().getName())); + loadUsers(ph, getUsersFile()); + // transfer new data + resetUsers(); + for (User tempUser : ph.getUserList()) { + tempUser.clone(this); + } + this.removeUsersChangedFlag(); + this.setTimeStampUsers(getUsersFile().lastModified()); + + ph = null; + } catch (Exception ex) { + Logger.getLogger(WorldDataHolder.class.getName()).log(Level.WARNING, null, ex); + } + GroupManager.setLoaded(true); + GroupManager.getGMEventHandler().callEvent(GMSystemEvent.Action.RELOADED); + } + + public void loadGroups(File groupsFile) { + + GroupManager.setLoaded(false); + try { + setGroupsFile(groupsFile); + loadGroups(this, groupsFile); + } catch (FileNotFoundException e) { + e.printStackTrace(); + throw new IllegalArgumentException("The file which should contain groups does not exist!\n" + groupsFile.getPath()); + } catch (IOException e) { + e.printStackTrace(); + throw new IllegalArgumentException("Error accessing the groups file!\n" + groupsFile.getPath()); + } + + GroupManager.setLoaded(true); + } + + public void loadUsers(File usersFile) { + + GroupManager.setLoaded(false); + try { + setUsersFile(usersFile); + loadUsers(this, usersFile); + } catch (FileNotFoundException e) { + e.printStackTrace(); + throw new IllegalArgumentException("The file which should contain users does not exist!\n" + usersFile.getPath()); + } catch (IOException e) { + e.printStackTrace(); + throw new IllegalArgumentException("Error accessing the users file!\n" + usersFile.getPath()); + } + + GroupManager.setLoaded(true); + } + + /** + * Returns a NEW data holder containing data read from the files + * + * @param worldName + * @param groupsFile + * @param usersFile + * + * @throws FileNotFoundException + * @throws IOException + */ + public static WorldDataHolder load(String worldName, File groupsFile, File usersFile) throws FileNotFoundException, IOException { + + WorldDataHolder ph = new WorldDataHolder(worldName); + + GroupManager.setLoaded(false); + if (groupsFile != null) + loadGroups(ph, groupsFile); + if (usersFile != null) + loadUsers(ph, usersFile); + GroupManager.setLoaded(true); + + return ph; + } + + /** + * Updates the WorldDataHolder from the Groups file + * + * @param ph + * @param groupsFile + * + * @throws FileNotFoundException + * @throws IOException + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected static void loadGroups(WorldDataHolder ph, File groupsFile) throws FileNotFoundException, IOException { + + // READ GROUPS FILE + + Yaml yamlGroups = new Yaml(new SafeConstructor()); + Map groupsRootDataNode; + + if (!groupsFile.exists()) { + throw new IllegalArgumentException("The file which should contain groups does not exist!\n" + groupsFile.getPath()); + } + FileInputStream groupsInputStream = new FileInputStream(groupsFile); + try { + groupsRootDataNode = (Map) yamlGroups.load(new UnicodeReader(groupsInputStream)); + if (groupsRootDataNode == null) { + throw new NullPointerException(); + } + } catch (Exception ex) { + throw new IllegalArgumentException("The following file couldn't pass on Parser.\n" + groupsFile.getPath(), ex); + } finally { + groupsInputStream.close(); + } + + // PROCESS GROUPS FILE + + Map> inheritance = new HashMap>(); + Map allGroupsNode = null; + + /* + * Fetch all groups under the 'groups' entry. + */ + try { + allGroupsNode = (Map) groupsRootDataNode.get("groups"); + } catch (Exception ex) { + throw new IllegalArgumentException("Your " + groupsFile.getPath() + " file is invalid. See console for details.", ex); + } + + if (allGroupsNode == null) { + throw new IllegalArgumentException("You have no groups in " + groupsFile.getPath() + "."); + } + + Iterator groupItr = allGroupsNode.keySet().iterator(); + String groupKey; + Integer groupCount = 0; + + /* + * loop each group entry and process it's data. + */ + while (groupItr.hasNext()) { + + try { + groupCount++; + // Attempt to fetch the next group name. + groupKey = groupItr.next(); + } catch (Exception ex) { + throw new IllegalArgumentException("Invalid group name for group entry (" + groupCount + ") in file: " + groupsFile.getPath(), ex); + } + + /* + * Fetch this groups child nodes + */ + Map thisGroupNode = null; + + try { + thisGroupNode = (Map) allGroupsNode.get(groupKey); + } catch (Exception ex) { + throw new IllegalArgumentException("Invalid child nodes for group '" + groupKey + "' in file: " + groupsFile.getPath(), ex); + } + + /* + * Create a new group with this name in the assigned data source. + */ + Group thisGrp = ph.createGroup(groupKey); + + if (thisGrp == null) { + throw new IllegalArgumentException("I think this Group was declared more than once: " + groupKey + " in file: " + groupsFile.getPath()); + } + + // DEFAULT NODE + + Object nodeData = null; + try { + nodeData = thisGroupNode.get("default"); + } catch (Exception ex) { + throw new IllegalArgumentException("Bad format found in 'permissions' for group: " + groupKey + " in file: " + groupsFile.getPath()); + } + + if (nodeData == null) { + /* + * If no 'default' node is found do nothing. + */ + } else if ((Boolean.parseBoolean(nodeData.toString()))) { + /* + * Set this as the default group. Warn if some other group has + * already claimed that position. + */ + if (ph.getDefaultGroup() != null) { + GroupManager.logger.warning("The group '" + thisGrp.getName() + "' is claiming to be default where '" + ph.getDefaultGroup().getName() + "' already was."); + GroupManager.logger.warning("Overriding first default request in file: " + groupsFile.getPath()); + } + ph.setDefaultGroup(thisGrp); + } + + // PERMISSIONS NODE + + nodeData = null; + try { + nodeData = thisGroupNode.get("permissions"); + } catch (Exception ex) { + throw new IllegalArgumentException("Bad format found in 'permissions' for '" + groupKey + "' in file: " + groupsFile.getPath()); + } + + if (nodeData == null) { + /* + * If no permissions node is found, or it's empty do nothing. + */ + } else { + /* + * There is a permission list Which seems to hold some data + */ + if (nodeData instanceof List) { + /* + * Check each entry and add it as a new permission. + */ + try { + for (Object o : ((List) nodeData)) { + try { + /* + * Only add this permission if it's not empty. + */ + if (!o.toString().isEmpty()) + thisGrp.addPermission(o.toString()); + + } catch (NullPointerException ex) { + // Ignore this entry as it's null. It can be + // safely dropped + } + } + } catch (Exception ex) { + throw new IllegalArgumentException("Invalid formatting found in 'permissions' section for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath(), ex); + } + + } else if (nodeData instanceof String) { + /* + * Only add this permission if it's not empty. + */ + if (!nodeData.toString().isEmpty()) + thisGrp.addPermission((String) nodeData); + + } else { + throw new IllegalArgumentException("Unknown type of 'permissions' node(Should be String or List) for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath()); + } + /* + * Sort all permissions so they are in the correct order for + * checking. + */ + thisGrp.sortPermissions(); + } + + // INFO NODE + + nodeData = null; + try { + nodeData = thisGroupNode.get("info"); + } catch (Exception ex) { + throw new IllegalArgumentException("Bad format found in 'info' section for group: " + groupKey + " in file: " + groupsFile.getPath()); + } + + if (nodeData == null) { + /* + * No info section was found, so leave all variables as + * defaults. + */ + GroupManager.logger.warning("The group '" + thisGrp.getName() + "' has no 'info' section!"); + GroupManager.logger.warning("Using default values: " + groupsFile.getPath()); + + } else if (nodeData instanceof Map) { + try { + if (nodeData != null) { + thisGrp.setVariables((Map) nodeData); + } + } catch (Exception ex) { + throw new IllegalArgumentException("Invalid formatting found in 'info' section for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath(), ex); + } + + } else + throw new IllegalArgumentException("Unknown entry found in 'info' section for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath()); + + // INHERITANCE NODE + + nodeData = null; + try { + nodeData = thisGroupNode.get("inheritance"); + } catch (Exception ex) { + throw new IllegalArgumentException("Bad format found in 'inheritance' section for group: " + groupKey + " in file: " + groupsFile.getPath()); + } + + if (nodeData == null || nodeData instanceof List) { + if (nodeData == null) { + /* + * If no inheritance node is found, or it's empty do + * nothing. + */ + } else if (nodeData instanceof List) { + + try { + for (String grp : (List) nodeData) { + if (inheritance.get(groupKey) == null) { + inheritance.put(groupKey, new ArrayList()); + } + inheritance.get(groupKey).add(grp); + } + + } catch (Exception ex) { + throw new IllegalArgumentException("Invalid formatting found in 'inheritance' section for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath(), ex); + } + + } + } else + throw new IllegalArgumentException("Unknown entry found in 'inheritance' section for group: " + thisGrp.getName() + " in file: " + groupsFile.getPath()); + + // END GROUP + + } + + if (ph.getDefaultGroup() == null) { + throw new IllegalArgumentException("There was no Default Group declared in file: " + groupsFile.getPath()); + } + + /* + * Build the inheritance map and recored any errors + */ + for (String group : inheritance.keySet()) { + List inheritedList = inheritance.get(group); + Group thisGroup = ph.getGroup(group); + if (thisGroup != null) + for (String inheritedKey : inheritedList) { + if (inheritedKey != null) { + Group inheritedGroup = ph.getGroup(inheritedKey); + if (inheritedGroup != null) { + thisGroup.addInherits(inheritedGroup); + } else + GroupManager.logger.warning("Inherited group '" + inheritedKey + "' not found for group " + thisGroup.getName() + ". Ignoring entry in file: " + groupsFile.getPath()); + } + } + } + + ph.removeGroupsChangedFlag(); + // Update the LastModified time. + ph.setGroupsFile(groupsFile); + ph.setTimeStampGroups(groupsFile.lastModified()); + + // return ph; + } + + /** + * Updates the WorldDataHolder from the Users file + * + * @param ph + * @param usersFile + * + * @throws FileNotFoundException + * @throws IOException + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected static void loadUsers(WorldDataHolder ph, File usersFile) throws FileNotFoundException, IOException { + + // READ USERS FILE + Yaml yamlUsers = new Yaml(new SafeConstructor()); + Map usersRootDataNode; + if (!usersFile.exists()) { + throw new IllegalArgumentException("The file which should contain users does not exist!\n" + usersFile.getPath()); + } + FileInputStream usersInputStream = new FileInputStream(usersFile); + try { + usersRootDataNode = (Map) yamlUsers.load(new UnicodeReader(usersInputStream)); + if (usersRootDataNode == null) { + throw new NullPointerException(); + } + } catch (Exception ex) { + throw new IllegalArgumentException("The following file couldn't pass on Parser.\n" + usersFile.getPath(), ex); + } finally { + usersInputStream.close(); + } + + // PROCESS USERS FILE + + Map allUsersNode = null; + + /* + * Fetch all child nodes under the 'users' entry. + */ + try { + allUsersNode = (Map) usersRootDataNode.get("users"); + } catch (Exception ex) { + throw new IllegalArgumentException("Your " + usersFile.getPath() + " file is invalid. See console for details.", ex); + } + + // Load users if the file is NOT empty + + if (allUsersNode != null) { + + Iterator usersItr = allUsersNode.keySet().iterator(); + String usersKey; + Object node; + Integer userCount = 0; + + while (usersItr.hasNext()) { + try { + userCount++; + // Attempt to fetch the next user name. + node = usersItr.next(); + if (node instanceof Integer) + usersKey = Integer.toString((Integer) node); + else + usersKey = node.toString(); + + } catch (Exception ex) { + throw new IllegalArgumentException("Invalid node type for user entry (" + userCount + ") in file: " + usersFile.getPath(), ex); + } + + Map thisUserNode = null; + try { + thisUserNode = (Map) allUsersNode.get(node); + } catch (Exception ex) { + throw new IllegalArgumentException("Bad format found for user: " + usersKey + " in file: " + usersFile.getPath()); + } + + User thisUser = ph.createUser(usersKey); + if (thisUser == null) { + throw new IllegalArgumentException("I think this user was declared more than once: " + usersKey + " in file: " + usersFile.getPath()); + } + + // LASTNAME NODES + + Object nodeData = null; + try { + + nodeData = thisUserNode.get("lastname"); + + } catch (Exception ex) { + throw new IllegalArgumentException("Bad format found in 'subgroups' for user: " + usersKey + " in file: " + usersFile.getPath()); + } + + if ((nodeData != null) && (nodeData instanceof String)) { + + thisUser.setLastName((String) nodeData); + + } + + // USER PERMISSIONS NODES + + nodeData = null; + try { + nodeData = thisUserNode.get("permissions"); + } catch (Exception ex) { + throw new IllegalArgumentException("Bad format found in 'permissions' for user: " + usersKey + " in file: " + usersFile.getPath()); + } + + if (nodeData == null) { + /* + * If no permissions node is found, or it's empty do + * nothing. + */ + } else { + try { + if (nodeData instanceof List) { + for (Object o : ((List) nodeData)) { + /* + * Only add this permission if it's not empty + */ + if (!o.toString().isEmpty()) { + thisUser.addPermission(o.toString()); + } + } + } else if (nodeData instanceof String) { + + /* + * Only add this permission if it's not empty + */ + if (!nodeData.toString().isEmpty()) { + thisUser.addPermission(nodeData.toString()); + } + + } + } catch (NullPointerException e) { + // Ignore this entry as it's null. + } + thisUser.sortPermissions(); + } + + // SUBGROUPS NODES + + nodeData = null; + try { + nodeData = thisUserNode.get("subgroups"); + } catch (Exception ex) { + throw new IllegalArgumentException("Bad format found in 'subgroups' for user: " + usersKey + " in file: " + usersFile.getPath()); + } + + if (nodeData == null) { + /* + * If no subgroups node is found, or it's empty do nothing. + */ + } else if (nodeData instanceof List) { + for (Object o : ((List) nodeData)) { + if (o == null) { + GroupManager.logger.warning("Invalid Subgroup data for user: " + thisUser.getLastName() + ". Ignoring entry in file: " + usersFile.getPath()); + } else { + Group subGrp = ph.getGroup(o.toString()); + if (subGrp != null) { + thisUser.addSubGroup(subGrp); + } else { + GroupManager.logger.warning("Subgroup '" + o.toString() + "' not found for user: " + thisUser.getLastName() + ". Ignoring entry in file: " + usersFile.getPath()); + } + } + } + } else if (nodeData instanceof String) { + Group subGrp = ph.getGroup(nodeData.toString()); + if (subGrp != null) { + thisUser.addSubGroup(subGrp); + } else { + GroupManager.logger.warning("Subgroup '" + nodeData.toString() + "' not found for user: " + thisUser.getLastName() + ". Ignoring entry in file: " + usersFile.getPath()); + } + } + + // USER INFO NODE + + nodeData = null; + try { + nodeData = thisUserNode.get("info"); + } catch (Exception ex) { + throw new IllegalArgumentException("Bad format found in 'info' section for user: " + usersKey + " in file: " + usersFile.getPath()); + } + + if (nodeData == null) { + /* + * If no info node is found, or it's empty do nothing. + */ + } else if (nodeData instanceof Map) { + thisUser.setVariables((Map) nodeData); + + } else + throw new IllegalArgumentException("Unknown entry found in 'info' section for user: " + thisUser.getLastName() + " in file: " + usersFile.getPath()); + + // END INFO NODE + + // PRIMARY GROUP + + nodeData = null; + try { + nodeData = thisUserNode.get("group"); + } catch (Exception ex) { + throw new IllegalArgumentException("Bad format found in 'group' section for user: " + usersKey + " in file: " + usersFile.getPath()); + } + + if (nodeData != null) { + Group hisGroup = ph.getGroup(nodeData.toString()); + if (hisGroup == null) { + GroupManager.logger.warning("There is no group " + thisUserNode.get("group").toString() + ", as stated for player " + thisUser.getLastName() + ": Set to '" + ph.getDefaultGroup().getName() + "' for file: " + + usersFile.getPath()); + hisGroup = ph.getDefaultGroup(); + } + thisUser.setGroup(hisGroup); + } else { + thisUser.setGroup(ph.getDefaultGroup()); + } + } + } + + ph.removeUsersChangedFlag(); + // Update the LastModified time. + ph.setUsersFile(usersFile); + ph.setTimeStampUsers(usersFile.lastModified()); + } + + /** + * Write a dataHolder in a specified file + * + * @param ph + * @param groupsFile + */ + public static void writeGroups(WorldDataHolder ph, File groupsFile) { + + Map root = new HashMap(); + + Map groupsMap = new HashMap(); + + root.put("groups", groupsMap); + synchronized (ph.getGroups()) { + for (String groupKey : ph.getGroups().keySet()) { + Group group = ph.getGroups().get(groupKey); + + Map aGroupMap = new HashMap(); + groupsMap.put(group.getName(), aGroupMap); + + if (ph.getDefaultGroup() == null) { + GroupManager.logger.severe("There is no default group for world: " + ph.getName()); + } + aGroupMap.put("default", group.equals(ph.getDefaultGroup())); + + Map infoMap = new HashMap(); + aGroupMap.put("info", infoMap); + + for (String infoKey : group.getVariables().getVarKeyList()) { + infoMap.put(infoKey, group.getVariables().getVarObject(infoKey)); + } + + aGroupMap.put("inheritance", group.getInherits()); + + aGroupMap.put("permissions", group.getPermissionList()); + } + } + + if (!root.isEmpty()) { + DumperOptions opt = new DumperOptions(); + opt.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + final Yaml yaml = new Yaml(opt); + try { + OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(groupsFile), "UTF-8"); + + String newLine = System.getProperty("line.separator"); + + out.write("# Group inheritance" + newLine); + out.write("#" + newLine); + out.write("# Any inherited groups prefixed with a g: are global groups" + newLine); + out.write("# and are inherited from the GlobalGroups.yml." + newLine); + out.write("#" + newLine); + out.write("# Groups without the g: prefix are groups local to this world" + newLine); + out.write("# and are defined in the this groups.yml file." + newLine); + out.write("#" + newLine); + out.write("# Local group inheritances define your promotion tree when using 'manpromote/mandemote'" + newLine); + out.write(newLine); + + yaml.dump(root, out); + out.close(); + } catch (UnsupportedEncodingException ex) { + } catch (FileNotFoundException ex) { + } catch (IOException e) { + } + } + + // Update the LastModified time. + ph.setGroupsFile(groupsFile); + ph.setTimeStampGroups(groupsFile.lastModified()); + ph.removeGroupsChangedFlag(); + + if (GroupManager.isLoaded()) + GroupManager.getGMEventHandler().callEvent(GMSystemEvent.Action.SAVED); + + /* + * FileWriter tx = null; try { tx = new FileWriter(groupsFile, false); + * tx.write(yaml.dump(root)); tx.flush(); } catch (Exception e) { } + * finally { try { tx.close(); } catch (IOException ex) { } } + */ + } + + /** + * Write a dataHolder in a specified file + * + * @param ph + * @param usersFile + */ + public static void writeUsers(WorldDataHolder ph, File usersFile) { + + Map root = new HashMap(); + LinkedHashMap usersMap = new LinkedHashMap(); + + root.put("users", usersMap); + synchronized (ph.getUsers()) { + + // A sorted list of users. + for (String userKey : new TreeSet(ph.getUsers().keySet())) { + User user = ph.getUsers().get(userKey); + if ((user.getGroup() == null || user.getGroup().equals(ph.getDefaultGroup())) && user.getPermissionList().isEmpty() && user.getVariables().isEmpty() && user.isSubGroupsEmpty()) { + continue; + } + + LinkedHashMap aUserMap = new LinkedHashMap(); + usersMap.put(user.getUUID(), aUserMap); + + if (!user.getUUID().equalsIgnoreCase(user.getLastName())) { + aUserMap.put("lastname", user.getLastName()); + } + + // GROUP NODE + if (user.getGroup() == null) { + aUserMap.put("group", ph.getDefaultGroup().getName()); + } else { + aUserMap.put("group", user.getGroup().getName()); + } + + // SUBGROUPS NODE + aUserMap.put("subgroups", user.subGroupListStringCopy()); + + // PERMISSIONS NODE + aUserMap.put("permissions", user.getPermissionList()); + + // USER INFO NODE - BETA + if (user.getVariables().getSize() > 0) { + Map infoMap = new HashMap(); + aUserMap.put("info", infoMap); + for (String infoKey : user.getVariables().getVarKeyList()) { + infoMap.put(infoKey, user.getVariables().getVarObject(infoKey)); + } + } + // END USER INFO NODE - BETA + + } + } + + if (!root.isEmpty()) { + DumperOptions opt = new DumperOptions(); + opt.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + final Yaml yaml = new Yaml(opt); + try { + OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(usersFile), "UTF-8"); + yaml.dump(root, out); + out.close(); + } catch (UnsupportedEncodingException ex) { + } catch (FileNotFoundException ex) { + } catch (IOException e) { + } + } + + // Update the LastModified time. + ph.setUsersFile(usersFile); + ph.setTimeStampUsers(usersFile.lastModified()); + ph.removeUsersChangedFlag(); + + if (GroupManager.isLoaded()) + GroupManager.getGMEventHandler().callEvent(GMSystemEvent.Action.SAVED); + + /* + * FileWriter tx = null; try { tx = new FileWriter(usersFile, false); + * tx.write(yaml.dump(root)); tx.flush(); } catch (Exception e) { } + * finally { try { tx.close(); } catch (IOException ex) { } } + */ + } + + /** + * Don't use this. Unless you want to make this plugin to interact with + * original Nijikokun Permissions This method is supposed to make the + * original one reload the file, and propagate the changes made here. + * + * Prefer to use the AnjoCaido's fake version of Nijikokun's Permission + * plugin. The AnjoCaido's Permission can propagate the changes made on this + * plugin instantly, without need to save the file. + * + * @param server + * the server that holds the plugin + * @deprecated it is not used anymore... unless if you use original + * Permissions + */ + @Deprecated + public static void reloadOldPlugins(Server server) { + + // Only reload permissions + PluginManager pm = server.getPluginManager(); + Plugin[] plugins = pm.getPlugins(); + for (int i = 0; i < plugins.length; i++) { + // plugins[i].getConfiguration().load(); + try { + plugins[i].getClass().getMethod("setupPermissions").invoke(plugins[i]); + } catch (Exception ex) { + continue; + } + } + } + + /** + * @return the permissionsHandler + */ + public AnjoPermissionsHandler getPermissionsHandler() { + + if (permissionsHandler == null) { + permissionsHandler = new AnjoPermissionsHandler(this); + } + return permissionsHandler; + } + + /** + * @param haveUsersChanged + * the haveUsersChanged to set + */ + public void setUsersChanged(boolean haveUsersChanged) { + + users.setUsersChanged(haveUsersChanged); + } + + /** + * + * @return true if any user data has changed + */ + public boolean haveUsersChanged() { + + if (users.HaveUsersChanged()) { + return true; + } + synchronized (users.getUsers()) { + for (User u : users.getUsers().values()) { + if (u.isChanged()) { + return true; + } + } + } + return false; + } + + /** + * @param setGroupsChanged + * the haveGroupsChanged to set + */ + public void setGroupsChanged(boolean setGroupsChanged) { + + groups.setGroupsChanged(setGroupsChanged); + } + + /** + * + * @return true if any group data has changed. + */ + public boolean haveGroupsChanged() { + + if (groups.HaveGroupsChanged()) { + return true; + } + synchronized (groups.getGroups()) { + for (Group g : groups.getGroups().values()) { + if (g.isChanged()) { + return true; + } + } + } + return false; + } + + /** + * + */ + public void removeUsersChangedFlag() { + + setUsersChanged(false); + synchronized (getUsers()) { + for (User u : getUsers().values()) { + u.flagAsSaved(); + } + } + } + + /** + * + */ + public void removeGroupsChangedFlag() { + + setGroupsChanged(false); + synchronized (getGroups()) { + for (Group g : getGroups().values()) { + g.flagAsSaved(); + } + } + } + + /** + * @return the usersFile + */ + public File getUsersFile() { + + return users.getUsersFile(); + } + + /** + * @param file + * the usersFile to set + */ + public void setUsersFile(File file) { + + users.setUsersFile(file); + } + + /** + * @return the groupsFile + */ + public File getGroupsFile() { + + return groups.getGroupsFile(); + } + + /** + * @param file + * the groupsFile to set + */ + public void setGroupsFile(File file) { + + groups.setGroupsFile(file); + } + + /** + * @return the name + */ + public String getName() { + + return name; + } + + /** + * Resets Groups. + */ + public void resetGroups() { + + // setDefaultGroup(null); + groups.resetGroups(); + } + + /** + * Resets Users + */ + public void resetUsers() { + + users.resetUsers(); + } + + /** + * Note: Iteration over this object has to be synchronized! + * + * @return the groups + */ + public Map getGroups() { + + return groups.getGroups(); + } + + /** + * Note: Iteration over this object has to be synchronized! + * + * @return the users + */ + public Map getUsers() { + + return users.getUsers(); + } + + /** + * @return the groups + */ + public GroupsDataHolder getGroupsObject() { + + return groups; + } + + /** + * @param groupsDataHolder + * the GroupsDataHolder to set + */ + public void setGroupsObject(GroupsDataHolder groupsDataHolder) { + + groups = groupsDataHolder; + } + + /** + * @return the users + */ + public UsersDataHolder getUsersObject() { + + return users; + } + + /** + * @param usersDataHolder + * the UsersDataHolder to set + */ + public void setUsersObject(UsersDataHolder usersDataHolder) { + + users = usersDataHolder; + } + + /** + * @return the timeStampGroups + */ + public long getTimeStampGroups() { + + return groups.getTimeStampGroups(); + } + + /** + * @return the timeStampUsers + */ + public long getTimeStampUsers() { + + return users.getTimeStampUsers(); + } + + /** + * @param timeStampGroups + * the timeStampGroups to set + */ + protected void setTimeStampGroups(long timeStampGroups) { + + groups.setTimeStampGroups(timeStampGroups); + } + + /** + * @param timeStampUsers + * the timeStampUsers to set + */ + protected void setTimeStampUsers(long timeStampUsers) { + + users.setTimeStampUsers(timeStampUsers); + } + + public void setTimeStamps() { + + if (getGroupsFile() != null) + setTimeStampGroups(getGroupsFile().lastModified()); + if (getUsersFile() != null) + setTimeStampUsers(getUsersFile().lastModified()); + } + +} diff --git a/src/main/java/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java b/src/main/java/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java new file mode 100644 index 0000000..6742406 --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/dataholder/worlds/WorldsHolder.java @@ -0,0 +1,794 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.anjocaido.groupmanager.dataholder.worlds; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.anjocaido.groupmanager.GroupManager; +import org.anjocaido.groupmanager.dataholder.WorldDataHolder; +import org.anjocaido.groupmanager.dataholder.OverloadedWorldHolder; +import org.anjocaido.groupmanager.permissions.AnjoPermissionsHandler; +import org.anjocaido.groupmanager.utils.Tasks; +import org.bukkit.World; +import org.bukkit.entity.Player; + +/** + * + * @author gabrielcouto + */ +public class WorldsHolder { + + /** + * Map with instances of loaded worlds. + */ + private Map worldsData = new HashMap(); + + /** + * Map of mirrors: + * The key is the mirror. + * The object is the mirrored. + * + * Mirror shows the same data of mirrored. + */ + private Map mirrorsGroup = new HashMap(); + private Map mirrorsUser = new HashMap(); + + private String serverDefaultWorldName; + private GroupManager plugin; + private File worldsFolder; + + /** + * + * @param plugin + */ + public WorldsHolder(GroupManager plugin) { + + this.plugin = plugin; + resetWorldsHolder(); + } + + /** + * @return the mirrorsGroup + */ + public Map getMirrorsGroup() { + + return mirrorsGroup; + } + + /** + * @return the mirrorsUser + */ + public Map getMirrorsUser() { + + return mirrorsUser; + } + + public boolean isWorldKnown(String name) { + + return worldsData.containsKey(name.toLowerCase()); + } + + public void resetWorldsHolder() { + + worldsData = new HashMap(); + mirrorsGroup = new HashMap(); + mirrorsUser = new HashMap(); + + // Setup folders and check files exist for the primary world + verifyFirstRun(); + initialLoad(); + if (serverDefaultWorldName == null) + throw new IllegalStateException("There is no default group! OMG!"); + } + + private void initialLoad() { + + // load the initial world + initialWorldLoading(); + // Configure and load any mirrors and additional worlds as defined in config.yml + mirrorSetUp(); + // search the worlds folder for any manually created worlds (not listed in config.yml) + loadAllSearchedWorlds(); + } + + private void initialWorldLoading() { + + // Load the default world + loadWorld(serverDefaultWorldName); + // defaultWorld = getUpdatedWorldData(serverDefaultWorldName); + } + + private void loadAllSearchedWorlds() { + + /* + * Read all known worlds from Bukkit Create the data files if they don't + * already exist, and they are not mirrored. + */ + for (World world : plugin.getServer().getWorlds()) { + GroupManager.logger.log(Level.FINE, "Checking data for " + world.getName() + "."); + if ((!worldsData.containsKey(world.getName().toLowerCase())) && ((!mirrorsGroup.containsKey(world.getName().toLowerCase())) || (!mirrorsUser.containsKey(world.getName().toLowerCase())))) { + + if (worldsData.containsKey("all_unnamed_worlds")) { + + String usersMirror = mirrorsUser.get("all_unnamed_worlds"); + String groupsMirror = mirrorsGroup.get("all_unnamed_worlds"); + + if (usersMirror != null) + mirrorsUser.put(world.getName().toLowerCase(), usersMirror); + + if (groupsMirror != null) + mirrorsGroup.put(world.getName().toLowerCase(), groupsMirror); + + } + + GroupManager.logger.log(Level.FINE, "Creating folders for " + world.getName() + "."); + setupWorldFolder(world.getName()); + } + } + /* + * Loop over all folders within the worlds folder and attempt to load + * the world data + */ + for (File folder : worldsFolder.listFiles()) { + if (folder.isDirectory() && !folder.getName().startsWith(".")) { + GroupManager.logger.info("World Found: " + folder.getName()); + + /* + * don't load any worlds which are already loaded or fully + * mirrored worlds that don't need data. + */ + if (!worldsData.containsKey(folder.getName().toLowerCase()) && ((!mirrorsGroup.containsKey(folder.getName().toLowerCase())) || (!mirrorsUser.containsKey(folder.getName().toLowerCase())))) { + /* + * Call setupWorldFolder to check case sensitivity and + * convert to lower case, before we attempt to load this + * world. + */ + setupWorldFolder(folder.getName()); + loadWorld(folder.getName().toLowerCase()); + } + + } + } + } + + @SuppressWarnings("rawtypes") + public void mirrorSetUp() { + + mirrorsGroup.clear(); + mirrorsUser.clear(); + Map mirrorsMap = plugin.getGMConfig().getMirrorsMap(); + + HashSet mirroredWorlds = new HashSet(); + + if (mirrorsMap != null) { + for (String source : mirrorsMap.keySet()) { + // Make sure all non mirrored worlds have a set of data files. + setupWorldFolder(source); + // Load the world data + if (!worldsData.containsKey(source.toLowerCase())) + loadWorld(source); + + if (mirrorsMap.get(source) instanceof ArrayList) { + ArrayList mirrorList = (ArrayList) mirrorsMap.get(source); + + // These worlds fully mirror their parent + for (Object o : mirrorList) { + String world = o.toString().toLowerCase(); + if (!world.equalsIgnoreCase(serverDefaultWorldName)) { + try { + mirrorsGroup.remove(world); + mirrorsUser.remove(world); + } catch (Exception e) { + } + mirrorsGroup.put(world, getWorldData(source).getName()); + mirrorsUser.put(world, getWorldData(source).getName()); + + // Track this world so we can create a datasource for it later + mirroredWorlds.add(o.toString()); + + } else + GroupManager.logger.log(Level.WARNING, "Mirroring error with " + o.toString() + ". Recursive loop detected!"); + } + } else if (mirrorsMap.get(source) instanceof Map) { + Map subSection = (Map) mirrorsMap.get(source); + + for (Object key : subSection.keySet()) { + + if (!((String) key).equalsIgnoreCase(serverDefaultWorldName)) { + + if (subSection.get(key) instanceof ArrayList) { + ArrayList mirrorList = (ArrayList) subSection.get(key); + + // These worlds have defined mirroring + for (Object o : mirrorList) { + String type = o.toString().toLowerCase(); + try { + if (type.equals("groups")) + mirrorsGroup.remove(((String) key).toLowerCase()); + + if (type.equals("users")) + mirrorsUser.remove(((String) key).toLowerCase()); + + } catch (Exception e) { + } + if (type.equals("groups")) { + mirrorsGroup.put(((String) key).toLowerCase(), getWorldData(source).getName()); + GroupManager.logger.log(Level.FINE, "Adding groups mirror for " + key + "."); + } + + if (type.equals("users")) { + mirrorsUser.put(((String) key).toLowerCase(), getWorldData(source).getName()); + GroupManager.logger.log(Level.FINE, "Adding users mirror for " + key + "."); + } + } + + // Track this world so we can create a datasource for it later + mirroredWorlds.add((String) key); + + } else + throw new IllegalStateException("Unknown mirroring format for " + (String) key); + + } else { + GroupManager.logger.log(Level.WARNING, "Mirroring error with " + (String) key + ". Recursive loop detected!"); + } + + } + } + } + + // Create a datasource for any worlds not already loaded + for (String world : mirroredWorlds) { + if (!worldsData.containsKey(world.toLowerCase())) { + GroupManager.logger.log(Level.FINE, "No data for " + world + "."); + setupWorldFolder(world); + loadWorld(world, true); + } + } + } + } + + /** + * + */ + public void reloadAll() { + + // Load global groups + GroupManager.getGlobalGroups().load(); + + ArrayList alreadyDone = new ArrayList(); + for (WorldDataHolder w : worldsData.values()) { + if (alreadyDone.contains(w)) { + continue; + } + if (!mirrorsGroup.containsKey(w.getName().toLowerCase())) + w.reloadGroups(); + if (!mirrorsUser.containsKey(w.getName().toLowerCase())) + w.reloadUsers(); + + alreadyDone.add(w); + } + + } + + /** + * + * @param worldName + */ + public void reloadWorld(String worldName) { + + if (!mirrorsGroup.containsKey(worldName.toLowerCase())) + getWorldData(worldName).reloadGroups(); + if (!mirrorsUser.containsKey(worldName.toLowerCase())) + getWorldData(worldName).reloadUsers(); + } + + /** + * Wrapper to retain backwards compatibility + * (call this function to auto overwrite files) + */ + public void saveChanges() { + + saveChanges(true); + } + + /** + * + */ + public boolean saveChanges(boolean overwrite) { + + boolean changed = false; + ArrayList alreadyDone = new ArrayList(); + Tasks.removeOldFiles(plugin, plugin.getBackupFolder()); + + // Write Global Groups + if (GroupManager.getGlobalGroups().haveGroupsChanged()) { + GroupManager.getGlobalGroups().writeGroups(overwrite); + } else { + if (GroupManager.getGlobalGroups().getTimeStampGroups() < GroupManager.getGlobalGroups().getGlobalGroupsFile().lastModified()) { + System.out.print("Newer GlobalGroups file found (Loading changes)!"); + GroupManager.getGlobalGroups().load(); + } + } + + for (OverloadedWorldHolder w : worldsData.values()) { + if (alreadyDone.contains(w)) { + continue; + } + if (w == null) { + GroupManager.logger.severe("WHAT HAPPENED?"); + continue; + } + if (!mirrorsGroup.containsKey(w.getName().toLowerCase())) + if (w.haveGroupsChanged()) { + if (overwrite || (!overwrite && (w.getTimeStampGroups() >= w.getGroupsFile().lastModified()))) { + // Backup Groups file + backupFile(w, true); + + WorldDataHolder.writeGroups(w, w.getGroupsFile()); + changed = true; + // w.removeGroupsChangedFlag(); + } else { + // Newer file found. + GroupManager.logger.log(Level.WARNING, "Newer Groups file found for " + w.getName() + ", but we have local changes!"); + throw new IllegalStateException("Unable to save unless you issue a '/mansave force'"); + } + } else { + // Check for newer file as no local changes. + if (w.getTimeStampGroups() < w.getGroupsFile().lastModified()) { + System.out.print("Newer Groups file found (Loading changes)!"); + // Backup Groups file + backupFile(w, true); + w.reloadGroups(); + changed = true; + } + } + if (!mirrorsUser.containsKey(w.getName().toLowerCase())) + if (w.haveUsersChanged()) { + if (overwrite || (!overwrite && (w.getTimeStampUsers() >= w.getUsersFile().lastModified()))) { + // Backup Users file + backupFile(w, false); + + WorldDataHolder.writeUsers(w, w.getUsersFile()); + changed = true; + // w.removeUsersChangedFlag(); + } else { + // Newer file found. + GroupManager.logger.log(Level.WARNING, "Newer Users file found for " + w.getName() + ", but we have local changes!"); + throw new IllegalStateException("Unable to save unless you issue a '/mansave force'"); + } + } else { + // Check for newer file as no local changes. + if (w.getTimeStampUsers() < w.getUsersFile().lastModified()) { + System.out.print("Newer Users file found (Loading changes)!"); + // Backup Users file + backupFile(w, false); + w.reloadUsers(); + changed = true; + } + } + alreadyDone.add(w); + } + return changed; + } + + /** + * Backup the Groups/Users file + * + * @param w + * @param groups + */ + private void backupFile(OverloadedWorldHolder w, Boolean groups) { + + File backupFile = new File(plugin.getBackupFolder(), "bkp_" + w.getName() + (groups ? "_g_" : "_u_") + Tasks.getDateString() + ".yml"); + try { + Tasks.copy((groups ? w.getGroupsFile() : w.getUsersFile()), backupFile); + } catch (IOException ex) { + GroupManager.logger.log(Level.SEVERE, null, ex); + } + } + + /** + * Returns the dataHolder for the given world. + * If the world is not on the worlds list, returns the default world + * holder. + * + * Mirrors return their parent world data. + * If no mirroring data it returns the default world. + * + * @param worldName + * @return OverloadedWorldHolder + */ + public OverloadedWorldHolder getWorldData(String worldName) { + + String worldNameLowered = worldName.toLowerCase(); + + // Find this worlds data + if (worldsData.containsKey(worldNameLowered)) + return getUpdatedWorldData(worldNameLowered); + + // Oddly no data source was found for this world so attempt to return the global mirror. + if (worldsData.containsKey("all_unnamed_worlds")) { + GroupManager.logger.finest("Requested world " + worldName + " not found or badly mirrored. Returning all_unnamed_worlds world..."); + return getUpdatedWorldData("all_unnamed_worlds"); + } + + // Oddly no data source or global mirror was found for this world so return the default. + GroupManager.logger.finest("Requested world " + worldName + " not found or badly mirrored. Returning default world..."); + return getDefaultWorld(); + } + + /** + * Get the requested world data and update it's dataSource to be relevant + * for this world + * + * @param worldName + * @return updated world holder + */ + private OverloadedWorldHolder getUpdatedWorldData(String worldName) { + + String worldNameLowered = worldName.toLowerCase(); + + if (worldsData.containsKey(worldNameLowered)) { + OverloadedWorldHolder data = worldsData.get(worldNameLowered); + data.updateDataSource(); + return data; + } + return null; + + } + + /** + * Do a matching of playerName, if its found only one player, do + * getWorldData(player) + * + * @param playerName + * @return null if matching returned no player, or more than one. + */ + public OverloadedWorldHolder getWorldDataByPlayerName(String playerName) { + + List matchPlayer = plugin.getServer().matchPlayer(playerName); + if (matchPlayer.size() == 1) { + return getWorldData(matchPlayer.get(0)); + } + return null; + } + + /** + * Retrieves the field player.getWorld().getName() and do + * getWorld(worldName) + * + * @param player + * @return OverloadedWorldHolder + */ + public OverloadedWorldHolder getWorldData(Player player) { + + return getWorldData(player.getWorld().getName()); + } + + /** + * It does getWorld(worldName).getPermissionsHandler() + * + * @param worldName + * @return AnjoPermissionsHandler + */ + public AnjoPermissionsHandler getWorldPermissions(String worldName) { + + return getWorldData(worldName).getPermissionsHandler(); + } + + /** + * Returns the PermissionsHandler for this player data + * + * @param player + * @return AnjoPermissionsHandler + */ + public AnjoPermissionsHandler getWorldPermissions(Player player) { + + return getWorldData(player).getPermissionsHandler(); + } + + /** + * Id does getWorldDataByPlayerName(playerName). + * If it doesnt return null, it will return result.getPermissionsHandler() + * + * @param playerName + * @return null if the player matching gone wrong. + */ + public AnjoPermissionsHandler getWorldPermissionsByPlayerName(String playerName) { + + WorldDataHolder dh = getWorldDataByPlayerName(playerName); + if (dh != null) { + return dh.getPermissionsHandler(); + } + return null; + } + + private void verifyFirstRun() { + + /* + * Do not use the folder name if this + * is a Bukkit Forge server. + */ + if (plugin.getServer().getName().equalsIgnoreCase("BukkitForge")) { + serverDefaultWorldName = "overworld"; + + } else { + Properties server = new Properties(); + try { + server.load(new FileInputStream(new File("server.properties"))); + serverDefaultWorldName = server.getProperty("level-name").toLowerCase(); + } catch (IOException ex) { + GroupManager.logger.log(Level.SEVERE, null, ex); + } + } + setupWorldFolder(serverDefaultWorldName); + + } + + public void setupWorldFolder(String worldName) { + + String worldNameLowered = worldName.toLowerCase(); + + worldsFolder = new File(plugin.getDataFolder(), "worlds"); + if (!worldsFolder.exists()) { + worldsFolder.mkdirs(); + } + + File defaultWorldFolder = new File(worldsFolder, worldNameLowered); + if ((!defaultWorldFolder.exists()) && ((!mirrorsGroup.containsKey(worldNameLowered))) || (!mirrorsUser.containsKey(worldNameLowered))) { + + /* + * check and convert all old case sensitive folders to lower case + */ + File casedWorldFolder = new File(worldsFolder, worldName); + if ((casedWorldFolder.exists()) && (casedWorldFolder.getName().toLowerCase().equals(worldNameLowered))) { + /* + * Rename the old folder to the new lower cased format + */ + casedWorldFolder.renameTo(new File(worldsFolder, worldNameLowered)); + } else { + /* + * Else we just create the folder + */ + defaultWorldFolder.mkdirs(); + } + } + if (defaultWorldFolder.exists()) { + if (!mirrorsGroup.containsKey(worldNameLowered)) { + File groupsFile = new File(defaultWorldFolder, "groups.yml"); + if (!groupsFile.exists() || groupsFile.length() == 0) { + + InputStream template = plugin.getResourceAsStream("groups.yml"); + try { + Tasks.copy(template, groupsFile); + } catch (IOException ex) { + GroupManager.logger.log(Level.SEVERE, null, ex); + } + } + } + + if (!mirrorsUser.containsKey(worldNameLowered)) { + File usersFile = new File(defaultWorldFolder, "users.yml"); + if (!usersFile.exists() || usersFile.length() == 0) { + + InputStream template = plugin.getResourceAsStream("users.yml"); + try { + Tasks.copy(template, usersFile); + } catch (IOException ex) { + GroupManager.logger.log(Level.SEVERE, null, ex); + } + + } + } + } + } + + /** + * Copies the specified world data to another world + * + * @param fromWorld + * @param toWorld + * @return true if successfully copied. + */ + public boolean cloneWorld(String fromWorld, String toWorld) { + + File fromWorldFolder = new File(worldsFolder, fromWorld.toLowerCase()); + File toWorldFolder = new File(worldsFolder, toWorld.toLowerCase()); + if (toWorldFolder.exists() || !fromWorldFolder.exists()) { + return false; + } + File fromWorldGroups = new File(fromWorldFolder, "groups.yml"); + File fromWorldUsers = new File(fromWorldFolder, "users.yml"); + if (!fromWorldGroups.exists() || !fromWorldUsers.exists()) { + return false; + } + File toWorldGroups = new File(toWorldFolder, "groups.yml"); + File toWorldUsers = new File(toWorldFolder, "users.yml"); + toWorldFolder.mkdirs(); + try { + Tasks.copy(fromWorldGroups, toWorldGroups); + Tasks.copy(fromWorldUsers, toWorldUsers); + } catch (IOException ex) { + Logger.getLogger(WorldsHolder.class.getName()).log(Level.SEVERE, null, ex); + return false; + } + return true; + } + + /** + * Wrapper for LoadWorld(String,Boolean) for backwards compatibility + * + * Load a world from file. + * If it already been loaded, summon reload method from dataHolder. + * + * @param worldName + */ + public void loadWorld(String worldName) { + + loadWorld(worldName, false); + } + + /** + * Load a world from file. + * If it already been loaded, summon reload method from dataHolder. + * + * @param worldName + */ + public void loadWorld(String worldName, Boolean isMirror) { + + String worldNameLowered = worldName.toLowerCase(); + + if (worldsData.containsKey(worldNameLowered)) { + worldsData.get(worldNameLowered).reload(); + return; + } + GroupManager.logger.finest("Trying to load world " + worldName + "..."); + File thisWorldFolder = new File(worldsFolder, worldNameLowered); + if ((isMirror) || (thisWorldFolder.exists() && thisWorldFolder.isDirectory())) { + + // Setup file handles, if not mirrored + File groupsFile = (mirrorsGroup.containsKey(worldNameLowered)) ? null : new File(thisWorldFolder, "groups.yml"); + File usersFile = (mirrorsUser.containsKey(worldNameLowered)) ? null : new File(thisWorldFolder, "users.yml"); + + if ((groupsFile != null) && (!groupsFile.exists())) { + throw new IllegalArgumentException("Groups file for world '" + worldName + "' doesnt exist: " + groupsFile.getPath()); + } + if ((usersFile != null) && (!usersFile.exists())) { + throw new IllegalArgumentException("Users file for world '" + worldName + "' doesnt exist: " + usersFile.getPath()); + } + + WorldDataHolder tempHolder = new WorldDataHolder(worldNameLowered); + + // Map the group object for any mirror + if (mirrorsGroup.containsKey(worldNameLowered)) + tempHolder.setGroupsObject(this.getWorldData(mirrorsGroup.get(worldNameLowered)).getGroupsObject()); + else + tempHolder.loadGroups(groupsFile); + + // Map the user object for any mirror + if (mirrorsUser.containsKey(worldNameLowered)) + tempHolder.setUsersObject(this.getWorldData(mirrorsUser.get(worldNameLowered)).getUsersObject()); + else + tempHolder.loadUsers(usersFile); + + OverloadedWorldHolder thisWorldData = new OverloadedWorldHolder(tempHolder); + + // null the object so we don't keep file handles open where we shouldn't + tempHolder = null; + + // Set the file TimeStamps as it will be default from the initial load. + thisWorldData.setTimeStamps(); + + if (thisWorldData != null) { + GroupManager.logger.finest("Successful load of world " + worldName + "..."); + worldsData.put(worldNameLowered, thisWorldData); + return; + } + + // GroupManager.logger.severe("Failed to load world " + worldName + "..."); + } + } + + /** + * Tells if the such world has been mapped. + * + * It will return true if world is a mirror. + * + * @param worldName + * @return true if world is loaded or mirrored. false if not listed + */ + public boolean isInList(String worldName) { + + if (worldsData.containsKey(worldName.toLowerCase()) || mirrorsGroup.containsKey(worldName.toLowerCase()) || mirrorsUser.containsKey(worldName.toLowerCase())) { + return true; + } + return false; + } + + /** + * Verify if world has it's own file permissions. + * + * @param worldName + * @return true if it has its own holder. false if not. + */ + public boolean hasOwnData(String worldName) { + + if (worldsData.containsKey(worldName.toLowerCase()) && (!mirrorsGroup.containsKey(worldName.toLowerCase()) || !mirrorsUser.containsKey(worldName.toLowerCase()))) { + return true; + } + return false; + } + + /** + * @return the defaultWorld + */ + public OverloadedWorldHolder getDefaultWorld() { + + return getUpdatedWorldData(serverDefaultWorldName); + } + + /** + * Returns all physically loaded worlds which have at least one of their own + * data sets for users or groups which isn't an identical mirror. + * + * @return ArrayList of all loaded worlds + */ + public ArrayList allWorldsDataList() { + + ArrayList list = new ArrayList(); + + for (String world : worldsData.keySet()) { + + if (!world.equalsIgnoreCase("all_unnamed_worlds")) { + + // Fetch the relevant world object + OverloadedWorldHolder data = getWorldData(world); + + if (!list.contains(data)) { + + String worldNameLowered = data.getName().toLowerCase(); + String usersMirror = mirrorsUser.get(worldNameLowered); + String groupsMirror = mirrorsGroup.get(worldNameLowered); + + // is users mirrored? + if (usersMirror != null) { + + // If both are mirrored + if (groupsMirror != null) { + + // if the data sources are the same, return the parent + if (usersMirror == groupsMirror) { + data = getWorldData(usersMirror.toLowerCase()); + + // Only add the parent if it's not already listed. + if (!list.contains(data)) + list.add(data); + + continue; + } + // Both data sources are mirrors, but they are from different parents + // so fall through to add the actual data object. + } + // Groups isn't a mirror so fall through to add this this worlds data source + } + + // users isn't mirrored so we need to add this worlds data source + list.add(data); + } + } + } + return list; + } +} diff --git a/src/main/java/org/anjocaido/groupmanager/events/GMGroupEvent.java b/src/main/java/org/anjocaido/groupmanager/events/GMGroupEvent.java new file mode 100644 index 0000000..1c2cbae --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/events/GMGroupEvent.java @@ -0,0 +1,92 @@ +package org.anjocaido.groupmanager.events; + +import org.anjocaido.groupmanager.GroupManager; +import org.anjocaido.groupmanager.data.Group; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +/** + * @author ElgarL + * + */ +public class GMGroupEvent extends Event { + + /** + * + */ + private static final HandlerList handlers = new HandlerList(); + + protected Group group; + + protected String groupName; + + ////////////////////////////// + + protected Action action; + + public GMGroupEvent(final Group group, final Action action) { + + super(); + + this.group = group; + this.action = action; + this.groupName = group.getName(); + } + + public GMGroupEvent(final String groupName, final Action action) { + + super(); + + this.groupName = groupName; + this.action = action; + } + + public static HandlerList getHandlerList() { + + return handlers; + } + + public Action getAction() { + + return this.action; + } + + public Group getGroup() { + + return group; + } + + public String getGroupName() { + + return groupName; + } + + @Override + public HandlerList getHandlers() { + + return handlers; + } + + public void schedule(final GMGroupEvent event) { + + synchronized (GroupManager.getGMEventHandler().getServer()) { + if (GroupManager.getGMEventHandler().getServer().getScheduler().scheduleSyncDelayedTask(GroupManager.getGMEventHandler().getPlugin(), new Runnable() { + + @Override + public void run() { + + GroupManager.getGMEventHandler().getServer().getPluginManager().callEvent(event); + } + }, 1) == -1) + GroupManager.logger.warning("Could not schedule GM Event."); + } + } + + public enum Action { + GROUP_PERMISSIONS_CHANGED, + GROUP_INHERITANCE_CHANGED, + GROUP_INFO_CHANGED, + GROUP_ADDED, + GROUP_REMOVED, + } +} \ No newline at end of file diff --git a/src/main/java/org/anjocaido/groupmanager/events/GMSystemEvent.java b/src/main/java/org/anjocaido/groupmanager/events/GMSystemEvent.java new file mode 100644 index 0000000..84c2fc0 --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/events/GMSystemEvent.java @@ -0,0 +1,66 @@ +package org.anjocaido.groupmanager.events; + +import org.anjocaido.groupmanager.GroupManager; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +/** + * @author ElgarL + * + */ +public class GMSystemEvent extends Event { + + /** + * + */ + private static final HandlerList handlers = new HandlerList(); + + @Override + public HandlerList getHandlers() { + + return handlers; + } + + public static HandlerList getHandlerList() { + + return handlers; + } + + ////////////////////////////// + + protected Action action; + + public GMSystemEvent(Action action) { + + super(); + + this.action = action; + } + + public Action getAction() { + + return this.action; + } + + public enum Action { + RELOADED, + SAVED, + DEFAULT_GROUP_CHANGED, + VALIDATE_TOGGLE, + } + + public void schedule(final GMSystemEvent event) { + + synchronized (GroupManager.getGMEventHandler().getServer()) { + if (GroupManager.getGMEventHandler().getServer().getScheduler().scheduleSyncDelayedTask(GroupManager.getGMEventHandler().getPlugin(), new Runnable() { + + @Override + public void run() { + + GroupManager.getGMEventHandler().getServer().getPluginManager().callEvent(event); + } + }, 1) == -1) + GroupManager.logger.warning("Could not schedule GM Event."); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/anjocaido/groupmanager/events/GMUserEvent.java b/src/main/java/org/anjocaido/groupmanager/events/GMUserEvent.java new file mode 100644 index 0000000..02f323c --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/events/GMUserEvent.java @@ -0,0 +1,94 @@ +package org.anjocaido.groupmanager.events; + +import org.anjocaido.groupmanager.GroupManager; +import org.anjocaido.groupmanager.data.User; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +/** + * @author ElgarL + * + */ +public class GMUserEvent extends Event { + + /** + * + */ + private static final HandlerList handlers = new HandlerList(); + + @Override + public HandlerList getHandlers() { + + return handlers; + } + + public static HandlerList getHandlerList() { + + return handlers; + } + + ////////////////////////////// + + protected User user; + + protected String userName; + + protected Action action; + + public GMUserEvent(User user, Action action) { + + super(); + + this.user = user; + this.action = action; + this.userName = user.getLastName(); + } + + public GMUserEvent(String userName, Action action) { + + super(); + + this.userName = userName; + this.action = action; + } + + public Action getAction() { + + return this.action; + } + + public User getUser() { + + return user; + } + + public String getUserName() { + + return userName; + } + + public enum Action { + USER_PERMISSIONS_CHANGED, + USER_INHERITANCE_CHANGED, + USER_INFO_CHANGED, + USER_GROUP_CHANGED, + USER_SUBGROUP_CHANGED, + USER_ADDED, + USER_REMOVED, + } + + public void schedule(final GMUserEvent event) { + + synchronized (GroupManager.getGMEventHandler().getServer()) { + if (GroupManager.getGMEventHandler().getServer().getScheduler().scheduleSyncDelayedTask(GroupManager.getGMEventHandler().getPlugin(), new Runnable() { + + @Override + public void run() { + + GroupManager.getGMEventHandler().getServer().getPluginManager().callEvent(event); + } + }, 1) == -1) + GroupManager.logger.warning("Could not schedule GM Event."); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/anjocaido/groupmanager/events/GMWorldListener.java b/src/main/java/org/anjocaido/groupmanager/events/GMWorldListener.java new file mode 100644 index 0000000..5199cdc --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/events/GMWorldListener.java @@ -0,0 +1,61 @@ +package org.anjocaido.groupmanager.events; + +import org.anjocaido.groupmanager.GroupManager; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.world.WorldInitEvent; + +/** + * @author ElgarL + * + * Handle new world creation from other plugins + * + */ +public class GMWorldListener implements Listener { + + private final GroupManager plugin; + + public GMWorldListener(GroupManager instance) { + + plugin = instance; + registerEvents(); + } + + private void registerEvents() { + + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onWorldInit(WorldInitEvent event) { + + String worldName = event.getWorld().getName(); + + if (GroupManager.isLoaded() && !plugin.getWorldsHolder().isInList(worldName)) { + GroupManager.logger.info("New world detected..."); + GroupManager.logger.info("Creating data for: " + worldName); + + if (plugin.getWorldsHolder().isWorldKnown("all_unnamed_worlds")) { + + String usersMirror = plugin.getWorldsHolder().getMirrorsUser().get("all_unnamed_worlds"); + String groupsMirror = plugin.getWorldsHolder().getMirrorsGroup().get("all_unnamed_worlds"); + + if (usersMirror != null) + plugin.getWorldsHolder().getMirrorsUser().put(worldName.toLowerCase(), usersMirror); + + if (groupsMirror != null) + plugin.getWorldsHolder().getMirrorsGroup().put(worldName.toLowerCase(), groupsMirror); + + } + + plugin.getWorldsHolder().setupWorldFolder(worldName); + plugin.getWorldsHolder().loadWorld(worldName); + + if (plugin.getWorldsHolder().isInList(worldName)) { + GroupManager.logger.info("Don't forget to configure/mirror this world in config.yml."); + } else + GroupManager.logger.severe("Failed to configure this world."); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/anjocaido/groupmanager/events/GroupManagerEventHandler.java b/src/main/java/org/anjocaido/groupmanager/events/GroupManagerEventHandler.java new file mode 100644 index 0000000..20845af --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/events/GroupManagerEventHandler.java @@ -0,0 +1,82 @@ +package org.anjocaido.groupmanager.events; + +import org.anjocaido.groupmanager.GroupManager; +import org.anjocaido.groupmanager.data.Group; +import org.anjocaido.groupmanager.data.User; +import org.bukkit.Server; + +/** + * @author ElgarL + * + * Handles all Event generation. + * + */ +public class GroupManagerEventHandler { + + private final Server server; + private final GroupManager plugin; + + public GroupManagerEventHandler(GroupManager plugin) { + + this.plugin = plugin; + this.server = plugin.getServer(); + + } + + protected void callEvent(GMGroupEvent event) { + + event.schedule(event); + } + + protected void callEvent(GMUserEvent event) { + + event.schedule(event); + } + + protected void callEvent(GMSystemEvent event) { + + event.schedule(event); + } + + public void callEvent(Group group, GMGroupEvent.Action action) { + + callEvent(new GMGroupEvent(group, action)); + } + + public void callEvent(String groupName, GMGroupEvent.Action action) { + + callEvent(new GMGroupEvent(groupName, action)); + } + + public void callEvent(User user, GMUserEvent.Action action) { + + callEvent(new GMUserEvent(user, action)); + } + + public void callEvent(String userName, GMUserEvent.Action action) { + + callEvent(new GMUserEvent(userName, action)); + } + + public void callEvent(GMSystemEvent.Action action) { + + callEvent(new GMSystemEvent(action)); + } + + /** + * @return the plugin + */ + public GroupManager getPlugin() { + + return plugin; + } + + /** + * @return the server + */ + public Server getServer() { + + return server; + } + +} \ No newline at end of file diff --git a/src/main/java/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java b/src/main/java/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java new file mode 100644 index 0000000..3caa449 --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/permissions/AnjoPermissionsHandler.java @@ -0,0 +1,1353 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.anjocaido.groupmanager.permissions; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.anjocaido.groupmanager.GroupManager; +import org.anjocaido.groupmanager.data.Group; +import org.anjocaido.groupmanager.data.User; +import org.anjocaido.groupmanager.dataholder.WorldDataHolder; +import org.anjocaido.groupmanager.utils.PermissionCheckResult; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +/** + * Everything here maintains the model created by Nijikokun + * + * But implemented to use GroupManager system. Which provides instant changes, + * without file access. + * + * It holds permissions only for one single world. + * + * @author gabrielcouto, ElgarL + */ +public class AnjoPermissionsHandler extends PermissionsReaderInterface { + + WorldDataHolder ph = null; + + /** + * It needs a WorldDataHolder to work with. + * + * @param holder + */ + public AnjoPermissionsHandler(WorldDataHolder holder) { + + ph = holder; + } + + /** + * A short name method, for permission method. + * + * @param player + * @param permission + * @return true if the player has the permission + */ + @Override + public boolean has(Player player, String permission) { + + return permission(player, permission); + } + + /** + * Checks if a player can use that permission node. + * + * @param player + * @param permission + * @return true if the player has the permission + */ + @Override + public boolean permission(Player player, String permission) { + + return checkUserPermission(ph.getUser(player.getName()).updatePlayer(player), permission); + } + + /** + * Checks if a player can use that permission node. + * + * @param playerName + * @param permission + * @return true if the player has the permission + */ + public boolean permission(String playerName, String permission) { + + return checkUserPermission(ph.getUser(playerName), permission); + } + + /** + * Returns the name of the group of that player name. + * + * @param userName + * @return String of players group name. + */ + @Override + public String getGroup(String userName) { + + return ph.getUser(userName).getGroup().getName(); + } + + /** + * Returns All permissions (including inheritance and sub groups) for the + * player, including child nodes from Bukkit. + * + * @param userName + * @return List of all players permissions. + */ + @Override + public List getAllPlayersPermissions(String userName) { + + List perms = new ArrayList(); + + perms.addAll(getAllPlayersPermissions(userName, true)); + + return perms; + } + + /** + * Returns All permissions (including inheritance and sub groups) for the + * player. With or without Bukkit child nodes. + * + * @param userName + * @return Set of all players permissions. + */ + @Override + public Set getAllPlayersPermissions(String userName, Boolean includeChildren) { + + Set playerPermArray = new LinkedHashSet(); + Set overrides = new LinkedHashSet(); + + // Add the players own permissions. + playerPermArray.addAll(populatePerms(ph.getUser(userName).getPermissionList(), includeChildren)); + + ArrayList alreadyProcessed = new ArrayList(); + + // fetch all group permissions + for (String group : getGroups(userName)) { + // Don't process a group more than once. + if (!alreadyProcessed.contains(group)) { + alreadyProcessed.add(group); + + Set groupPermArray = new LinkedHashSet(); + + if (group.startsWith("g:") && GroupManager.getGlobalGroups().hasGroup(group)) { + // GlobalGroups + groupPermArray = populatePerms(GroupManager.getGlobalGroups().getGroupsPermissions(group), includeChildren); + + } else { + // World Groups + groupPermArray = populatePerms(ph.getGroup(group).getPermissionList(), includeChildren); + } + + // Add all group permissions, unless negated by earlier permissions. + for (String perm : groupPermArray) { + boolean negated = (perm.startsWith("-")); + + // Overridden (Exception) permission defeats negation. + if (perm.startsWith("+")) { + overrides.add(perm.substring(1)); + continue; + } + + // Perm doesn't already exists and there is no negation for it + // or It's a negated perm where a normal perm doesn't exists (don't allow inheritance to negate higher perms) + if ((!negated && !playerPermArray.contains(perm) && !wildcardNegation(playerPermArray, perm)) + || (negated && !playerPermArray.contains(perm.substring(1)) && !wildcardNegation(playerPermArray, perm.substring(1)))) + playerPermArray.add(perm); + + } + } + + } + + // Process overridden permissions + + Iterator itr = overrides.iterator(); + + while (itr.hasNext()) { + + String node = itr.next(); + + if (playerPermArray.contains("-" + node)) { + playerPermArray.remove("-" + node); + } + + playerPermArray.add(node); + + } + + // Collections.sort(playerPermArray, StringPermissionComparator.getInstance()); + + return playerPermArray; + } + + /** + * Is there a direct or wildcard negation in the list which covers this permission node. + * + * @param playerPermArray + * @param node + * @return + */ + private boolean wildcardNegation(Set playerPermArray, String node) { + + /* + * Check for a negated parent with a wildcard or negated permission + */ + + if (playerPermArray.contains("-" + node)) + return true; + + final String[] parts = node.split("\\."); + final StringBuilder builder = new StringBuilder(node.length()); + for (String part : parts) { + builder.append('*'); + if (playerPermArray.contains("-" + builder.toString())) { + GroupManager.logger.fine("Wildcard Negation found for " + node); + return true; + } + + builder.deleteCharAt(builder.length() - 1); + builder.append(part).append('.'); + } + + /* + * No negated parent found so return false. + */ + GroupManager.logger.fine("No Negation found for " + node); + return false; + + } + + private Set populatePerms(List permsList, boolean includeChildren) { + + // Create a new array so it's modifiable. + List perms = new ArrayList(permsList); + Set permArray = new LinkedHashSet(); + Boolean allPerms = false; + + // Allow * node to populate ALL permissions to Bukkit. + if (perms.contains("*")) { + permArray.addAll(GroupManager.BukkitPermissions.getAllRegisteredPermissions(includeChildren)); + allPerms = true; + perms.remove("*"); + // Remove the no offline perms node as this should not be given. + perms.remove("groupmanager.noofflineperms"); + } + + for (String perm : perms) { + /** + * all permission sets are passed here pre-sorted, alphabetically. + * This means negated nodes will be processed before all permissions + * other than *. + */ + boolean negated = perm.startsWith("-"); + + if (!permArray.contains(perm)) { + permArray.add(perm); + + if ((negated) && (permArray.contains(perm.substring(1)))) + permArray.remove(perm.substring(1)); + + /** + * Process child nodes if required, + * or this is a negated node AND we used * to include all + * permissions, + * in which case we need to remove all children of that node. + */ + if ((includeChildren) || (negated && allPerms)) { + + Map children = GroupManager.BukkitPermissions.getAllChildren((negated ? perm.substring(1) : perm), new LinkedHashSet()); + + if (children != null) { + if (negated) + if (allPerms) { + + // Remove children of negated nodes + for (String child : children.keySet()) + if (children.get(child)) + if (permArray.contains(child)) + permArray.remove(child); + + } else { + + // Add child nodes + for (String child : children.keySet()) + if (children.get(child)) + if ((!permArray.contains(child)) && (!permArray.contains("-" + child))) + permArray.add(child); + } + } + } + } + } + + return permArray; + } + + /** + * Verify if player is in such group. It will check it's groups inheritance. + * + * So if you have a group Admin > Moderator + * + * And verify the player 'MyAdmin', which is Admin, it will return true for + * both Admin or Moderator groups. + * + * If you have a player 'MyModerator', which is Moderator, it will give + * false if you pass Admin in group parameter. + * + * @param name + * @param group + * @return true if in group (with inheritance) + */ + @Override + public boolean inGroup(String name, String group) { + + if (hasGroupInInheritance(ph.getUser(name).getGroup(), group)) { + return true; + } + for (Group subGroup : ph.getUser(name).subGroupListCopy()) { + if (hasGroupInInheritance(subGroup, group)) { + return true; + } + } + return false; + } + + /** + * Gets the appropriate prefix for the user. This method is a utility method + * for chat plugins to get the user's prefix without having to look at every + * one of the user's ancestors. Returns an empty string if user has no + * parent groups. + * + * @param user + * Player's name + * @return Player's prefix + */ + @Override + public String getUserPrefix(String user) { + + String prefix = ph.getUser(user).getVariables().getVarString("prefix"); + if (prefix.length() != 0) { + return prefix; + } + + return getGroupPrefix(getGroup(user)); + } + + /** + * Gets the appropriate prefix for the user. This method is a utility method + * for chat plugins to get the user's prefix without having to look at every + * one of the user's ancestors. Returns an empty string if user has no + * parent groups. + * + * @param user + * Player's name + * @return Player's prefix + */ + @Override + public String getUserSuffix(String user) { + + String suffix = ph.getUser(user).getVariables().getVarString("suffix"); + if (suffix.length() != 0) { + return suffix; + } + + return getGroupSuffix(getGroup(user)); + + } + + /** + * Gets name of the primary group of the user. Returns the name of the + * default group if user has no parent groups, or "Default" if there is no + * default group for that world. + * + * @param user + * Player's name + * @return Name of player's primary group + */ + public String getPrimaryGroup(String user) { + + return getGroup(user); + + } + + /** + * Check if user can build. Checks inheritance and subgroups. + * + * @param userName + * Player's name + * @return true if the user can build + */ + public boolean canUserBuild(String userName) { + + return getPermissionBoolean(userName, "build"); + + } + + /** + * Returns the String prefix for the given group + * + * @param groupName + * @return empty string if found none. + */ + @Override + public String getGroupPrefix(String groupName) { + + Group g = ph.getGroup(groupName); + if (g == null) { + return ""; + } + return g.getVariables().getVarString("prefix"); + } + + /** + * Return the suffix for the given group name + * + * @param groupName + * @return empty string if not found. + */ + @Override + public String getGroupSuffix(String groupName) { + + Group g = ph.getGroup(groupName); + if (g == null) { + return ""; + } + return g.getVariables().getVarString("suffix"); + } + + /** + * Checks the specified group for the Info Build node. Does NOT check + * inheritance + * + * @param groupName + * @return true if can build + */ + @Override + public boolean canGroupBuild(String groupName) { + + Group g = ph.getGroup(groupName); + if (g == null) { + return false; + } + return g.getVariables().getVarBoolean("build"); + } + + /** + * It returns a string variable value, set in the INFO node of the group. It + * will harvest inheritance for value. + * + * @param groupName + * @param variable + * @return null if no group with that variable is found. + */ + @Override + public String getGroupPermissionString(String groupName, String variable) { + + Group start = ph.getGroup(groupName); + if (start == null) { + return null; + } + Group result = nextGroupWithVariable(start, variable); + if (result == null) { + return null; + } + return result.getVariables().getVarString(variable); + } + + /** + * It returns a Integer variable value It will harvest inheritance for + * value. + * + * @param groupName + * @param variable + * @return -1 if none found or not parseable. + */ + @Override + public int getGroupPermissionInteger(String groupName, String variable) { + + Group start = ph.getGroup(groupName); + if (start == null) { + return -1; + } + Group result = nextGroupWithVariable(start, variable); + if (result == null) { + return -1; + } + return result.getVariables().getVarInteger(variable); + } + + /** + * Returns a boolean for given variable in INFO node. It will harvest + * inheritance for value. + * + * @param group + * @param variable + * @return false if not found/not parseable. + */ + @Override + public boolean getGroupPermissionBoolean(String group, String variable) { + + Group start = ph.getGroup(group); + if (start == null) { + return false; + } + Group result = nextGroupWithVariable(start, variable); + if (result == null) { + return false; + } + return result.getVariables().getVarBoolean(variable); + } + + /** + * Returns a double value for the given variable name in INFO node. It will + * harvest inheritance for value. + * + * @param group + * @param variable + * @return -1 if not found / not parseable. + */ + @Override + public double getGroupPermissionDouble(String group, String variable) { + + Group start = ph.getGroup(group); + if (start == null) { + return -1; + } + Group result = nextGroupWithVariable(start, variable); + if (result == null) { + return -1; + } + return result.getVariables().getVarDouble(variable); + } + + /** + * Returns the variable value of the user, in INFO node. + * + * @param user + * @param variable + * @return empty string if not found + */ + @Override + public String getUserPermissionString(String user, String variable) { + + User auser = ph.getUser(user); + if (auser == null) { + return ""; + } + return auser.getVariables().getVarString(variable); + } + + /** + * Returns the variable value of the user, in INFO node. + * + * @param user + * @param variable + * @return -1 if not found + */ + @Override + public int getUserPermissionInteger(String user, String variable) { + + User auser = ph.getUser(user); + if (auser == null) { + return -1; + } + return auser.getVariables().getVarInteger(variable); + } + + /** + * Returns the variable value of the user, in INFO node. + * + * @param user + * @param variable + * @return boolean value + */ + @Override + public boolean getUserPermissionBoolean(String user, String variable) { + + User auser = ph.getUser(user); + if (auser == null) { + return false; + } + return auser.getVariables().getVarBoolean(variable); + } + + /** + * Returns the variable value of the user, in INFO node. + * + * @param user + * @param variable + * @return -1 if not found + */ + @Override + public double getUserPermissionDouble(String user, String variable) { + + User auser = ph.getUser(user); + if (auser == null) { + return -1; + } + return auser.getVariables().getVarDouble(variable); + } + + /** + * Returns the variable value of the user, in INFO node. If not found, it + * will search for his Group variables. It will harvest the inheritance and + * subgroups. + * + * @param user + * @param variable + * @return empty string if not found + */ + @Override + public String getPermissionString(String user, String variable) { + + User auser = ph.getUser(user); + if (auser == null) { + return ""; + } + if (auser.getVariables().hasVar(variable)) { + return auser.getVariables().getVarString(variable); + } + Group start = auser.getGroup(); + if (start == null) { + return ""; + } + Group result = nextGroupWithVariable(start, variable); + if (result == null) { + // Check sub groups + if (!auser.isSubGroupsEmpty()) + for (Group subGroup : auser.subGroupListCopy()) { + result = nextGroupWithVariable(subGroup, variable); + // Found value? + if (result != null) + continue; + } + if (result == null) + return ""; + } + return result.getVariables().getVarString(variable); + // return getUserPermissionString(user, variable); + } + + /** + * Returns the variable value of the user, in INFO node. If not found, it + * will search for his Group variables. It will harvest the inheritance and + * subgroups. + * + * @param user + * @param variable + * @return -1 if not found + */ + @Override + public int getPermissionInteger(String user, String variable) { + + User auser = ph.getUser(user); + if (auser == null) { + return -1; + } + if (auser.getVariables().hasVar(variable)) { + return auser.getVariables().getVarInteger(variable); + } + Group start = auser.getGroup(); + if (start == null) { + return -1; + } + Group result = nextGroupWithVariable(start, variable); + if (result == null) { + // Check sub groups + if (!auser.isSubGroupsEmpty()) + for (Group subGroup : auser.subGroupListCopy()) { + result = nextGroupWithVariable(subGroup, variable); + // Found value? + if (result != null) + continue; + } + if (result == null) + return -1; + } + return result.getVariables().getVarInteger(variable); + // return getUserPermissionInteger(string, string1); + } + + /** + * Returns the variable value of the user, in INFO node. If not found, it + * will search for his Group variables. It will harvest the inheritance and + * subgroups. + * + * @param user + * @param variable + * @return false if not found or not parseable to true. + */ + @Override + public boolean getPermissionBoolean(String user, String variable) { + + User auser = ph.getUser(user); + if (auser == null) { + return false; + } + if (auser.getVariables().hasVar(variable)) { + return auser.getVariables().getVarBoolean(variable); + } + Group start = auser.getGroup(); + if (start == null) { + return false; + } + Group result = nextGroupWithVariable(start, variable); + if (result == null) { + // Check sub groups + if (!auser.isSubGroupsEmpty()) + for (Group subGroup : auser.subGroupListCopy()) { + result = nextGroupWithVariable(subGroup, variable); + // Found value? + if (result != null) + continue; + } + if (result == null) + return false; + } + return result.getVariables().getVarBoolean(variable); + // return getUserPermissionBoolean(user, string1); + } + + /** + * Returns the variable value of the user, in INFO node. If not found, it + * will search for his Group variables. It will harvest the inheritance and + * subgroups. + * + * @param user + * @param variable + * @return -1 if not found. + */ + @Override + public double getPermissionDouble(String user, String variable) { + + User auser = ph.getUser(user); + if (auser == null) { + return -1.0D; + } + if (auser.getVariables().hasVar(variable)) { + return auser.getVariables().getVarDouble(variable); + } + Group start = auser.getGroup(); + if (start == null) { + return -1.0D; + } + Group result = nextGroupWithVariable(start, variable); + if (result == null) { + // Check sub groups + if (!auser.isSubGroupsEmpty()) + for (Group subGroup : auser.subGroupListCopy()) { + result = nextGroupWithVariable(subGroup, variable); + // Found value? + if (result != null) + continue; + } + if (result == null) + return -1.0D; + } + return result.getVariables().getVarDouble(variable); + // return getUserPermissionDouble(string, string1); + } + + /** + * Does not include User's group permission + * + * @param user + * @param permission + * @return PermissionCheckResult + */ + public PermissionCheckResult checkUserOnlyPermission(User user, String permission) { + + user.sortPermissions(); + PermissionCheckResult result = new PermissionCheckResult(); + result.askedPermission = permission; + result.owner = user; + for (String access : user.getPermissionList()) { + result.resultType = comparePermissionString(access, permission); + if (result.resultType != PermissionCheckResult.Type.NOTFOUND) { + result.accessLevel = access; + return result; + } + } + result.resultType = PermissionCheckResult.Type.NOTFOUND; + return result; + } + + /** + * Returns the node responsible for that permission. Does not include User's + * group permission. + * + * @param group + * @param permission + * @return the node if permission is found. if not found, return null + */ + public PermissionCheckResult checkGroupOnlyPermission(Group group, String permission) { + + group.sortPermissions(); + PermissionCheckResult result = new PermissionCheckResult(); + result.owner = group; + result.askedPermission = permission; + for (String access : group.getPermissionList()) { + result.resultType = comparePermissionString(access, permission); + if (result.resultType != PermissionCheckResult.Type.NOTFOUND) { + result.accessLevel = access; + return result; + } + } + result.resultType = PermissionCheckResult.Type.NOTFOUND; + return result; + } + + /** + * Check permissions, including it's group and inheritance. + * + * @param user + * @param permission + * @return true if permission was found. false if not, or was negated. + */ + public boolean checkUserPermission(User user, String permission) { + + PermissionCheckResult result = checkFullGMPermission(user, permission, true); + if (result.resultType == PermissionCheckResult.Type.EXCEPTION || result.resultType == PermissionCheckResult.Type.FOUND) { + return true; + } + + return false; + } + + /** + * Do what checkUserPermission did before. But now returning a + * PermissionCheckResult. + * + * @param user + * @param targetPermission + * @return PermissionCheckResult + */ + public PermissionCheckResult checkFullUserPermission(User user, String targetPermission) { + + return checkFullGMPermission(user, targetPermission, true); + } + + /** + * Wrapper for offline server checks. + * Looks for the 'groupmanager.noofflineperms' permissions and reports no permissions on servers set to offline. + * + * Check user and groups with inheritance and Bukkit if bukkit = true return + * a PermissionCheckResult. + * + * @param user + * @param targetPermission + * @param checkBukkit + * @return PermissionCheckResult + */ + public PermissionCheckResult checkFullGMPermission(User user, String targetPermission, Boolean checkBukkit) { + + /* + * Report no permissions under the following conditions. + * + * We are in offline mode + * and the player has the 'groupmanager.noofflineperms' permission. + */ + if (user == null || targetPermission == null || targetPermission.isEmpty() + || (!Bukkit.getServer().getOnlineMode() && (checkPermission(user, "groupmanager.noofflineperms", false).resultType == PermissionCheckResult.Type.FOUND))) { + + PermissionCheckResult result = new PermissionCheckResult(); + result.accessLevel = targetPermission; + result.resultType = PermissionCheckResult.Type.NOTFOUND; + + return result; + } + + return checkPermission(user, targetPermission, checkBukkit); + } + + /** + * + * Check user and groups with inheritance and Bukkit if bukkit = true return + * a PermissionCheckResult. + * + * @param user + * @param targetPermission + * @param checkBukkit + * @return PermissionCheckResult + */ + private PermissionCheckResult checkPermission(User user, String targetPermission, Boolean checkBukkit) { + + PermissionCheckResult result = new PermissionCheckResult(); + result.accessLevel = targetPermission; + result.resultType = PermissionCheckResult.Type.NOTFOUND; + + if (checkBukkit) { + // Check Bukkit perms to support plugins which add perms via code + // (Heroes). + final Player player = user.getBukkitPlayer(); + // final Permission bukkitPerm = Bukkit.getPluginManager().getPermission(targetPermission); + if ((player != null) && player.hasPermission(targetPermission)) { + result.resultType = PermissionCheckResult.Type.FOUND; + result.owner = user; + return result; + } + } + + PermissionCheckResult resultUser = checkUserOnlyPermission(user, targetPermission); + if (resultUser.resultType != PermissionCheckResult.Type.NOTFOUND) { + + resultUser.accessLevel = targetPermission; + + if (resultUser.resultType == PermissionCheckResult.Type.EXCEPTION) { + return resultUser; + } + + result = resultUser; + + } + + // IT ONLY CHECKS GROUPS PERMISSIONS IF RESULT FOR USER IS NOT AN EXCEPTION + PermissionCheckResult resultGroup = checkGroupPermissionWithInheritance(user.getGroup(), targetPermission); + if (resultGroup.resultType != PermissionCheckResult.Type.NOTFOUND) { + + resultGroup.accessLevel = targetPermission; + + if (resultGroup.resultType == PermissionCheckResult.Type.EXCEPTION) { + return resultGroup; + } + + // Do not override higher level permissions with negations. + if (result.resultType == PermissionCheckResult.Type.NOTFOUND) { + result = resultGroup; + } + + } + + // Do we have a high level negation? + boolean negated = (result.resultType == PermissionCheckResult.Type.NEGATION); + + // SUBGROUPS CHECK + for (Group subGroup : user.subGroupListCopy()) { + + PermissionCheckResult resultSubGroup = checkGroupPermissionWithInheritance(subGroup, targetPermission); + if (resultSubGroup.resultType != PermissionCheckResult.Type.NOTFOUND) { + + resultSubGroup.accessLevel = targetPermission; + + // Allow exceptions to override higher level negations + // but low level negations can not remove higher level permissions. + if (resultSubGroup.resultType == PermissionCheckResult.Type.EXCEPTION) { + + return resultSubGroup; + + } else if ((resultSubGroup.resultType == PermissionCheckResult.Type.FOUND) && (result.resultType != PermissionCheckResult.Type.NEGATION) && !negated) { + + result = resultSubGroup; + + } else if ((resultSubGroup.resultType == PermissionCheckResult.Type.NEGATION) && !negated) { + + result = resultSubGroup; + } + + } + } + + // THEN IT RETURNS A NOT FOUND + // OR THE RESULT OF THE SUBGROUP SEARCH. + return result; + } + + /** + * Returns the next group, including inheritance, which contains that + * variable name. + * + * It does Breadth-first search + * + * @param start + * the starting group to look for + * @param targetVariable + * the variable name + * @return The group if found. Null if not. + */ + public Group nextGroupWithVariable(Group start, String targetVariable) { + + if (start == null || targetVariable == null) { + return null; + } + LinkedList stack = new LinkedList(); + ArrayList alreadyVisited = new ArrayList(); + stack.push(start); + alreadyVisited.add(start); + while (!stack.isEmpty()) { + Group now = stack.pop(); + if (now.getVariables().hasVar(targetVariable)) { + return now; + } + for (String sonName : now.getInherits()) { + Group son = ph.getGroup(sonName); + if (son != null && !alreadyVisited.contains(son)) { + stack.push(son); + alreadyVisited.add(son); + } + } + } + return null; + } + + /** + * Check if given group inherits another group. + * + * It does Breadth-first search + * + * @param start + * The group to start the search. + * @param askedGroup + * Name of the group you're looking for + * @return true if it inherits the group. + */ + public boolean hasGroupInInheritance(Group start, String askedGroup) { + + if (start == null || askedGroup == null) { + return false; + } + LinkedList stack = new LinkedList(); + ArrayList alreadyVisited = new ArrayList(); + stack.push(start); + alreadyVisited.add(start); + while (!stack.isEmpty()) { + Group now = stack.pop(); + if (now.getName().equalsIgnoreCase(askedGroup)) { + return true; + } + for (String sonName : now.getInherits()) { + Group son = ph.getGroup(sonName); + if (son != null && !alreadyVisited.contains(son)) { + stack.push(son); + alreadyVisited.add(son); + } + } + } + return false; + } + + /** + * Returns the result of permission check. Including inheritance. If found + * anything, the PermissionCheckResult that retuns will include the Group + * name, and the result type. Result types will be EXCEPTION, NEGATION, + * FOUND. + * + * If returned type NOTFOUND, the owner will be null, and ownerType too. + * + * It does Breadth-first search + * + * @param start + * @param targetPermission + * @return PermissionCheckResult + */ + public PermissionCheckResult checkGroupPermissionWithInheritance(Group start, String targetPermission) { + + if (start == null || targetPermission == null) { + return null; + } + + LinkedList stack = new LinkedList(); + List alreadyVisited = new ArrayList(); + PermissionCheckResult result = new PermissionCheckResult(); + + stack.push(start); + alreadyVisited.add(start); + + // Set defaults. + result.askedPermission = targetPermission; + result.resultType = PermissionCheckResult.Type.NOTFOUND; + + while (!stack.isEmpty()) { + Group now = stack.pop(); + PermissionCheckResult resultNow = checkGroupOnlyPermission(now, targetPermission); + + if (!resultNow.resultType.equals(PermissionCheckResult.Type.NOTFOUND)) { + + if (resultNow.resultType.equals(PermissionCheckResult.Type.EXCEPTION)) { + resultNow.accessLevel = targetPermission; + return resultNow; + } + + // Negation found so store for later + // as we need to continue looking for an Exception. + result = resultNow; + } + + for (String sonName : now.getInherits()) { + Group son = ph.getGroup(sonName); + if (son != null && !alreadyVisited.contains(son)) { + // Add rather than push to retain inheritance order. + stack.add(son); + alreadyVisited.add(son); + } + } + } + + return result; + } + + /** + * Return whole list of names of groups in a inheritance chain. Including a + * starting group. + * + * It does Breadth-first search. So closer groups will appear first in list. + * + * @param start + * @return the group that passed on test. null if no group passed. + */ + public ArrayList listAllGroupsInherited(Group start) { + + if (start == null) { + return null; + } + LinkedList stack = new LinkedList(); + ArrayList alreadyVisited = new ArrayList(); + stack.push(start); + alreadyVisited.add(start.getName()); + while (!stack.isEmpty()) { + Group now = stack.pop(); + for (String sonName : now.getInherits()) { + Group son = ph.getGroup(sonName); + if (son != null && !alreadyVisited.contains(son.getName())) { + stack.push(son); + alreadyVisited.add(son.getName()); + } + } + } + return alreadyVisited; + } + + /** + * Compare a user permission like 'myplugin.*' against a full plugin + * permission name, like 'myplugin.dosomething'. As the example above, will + * return true. + * + * Please sort permissions before sending them here. So negative tokens get + * priority. + * + * You must test if it start with negative outside this method. It will only + * tell if the nodes are matching or not. + * + * Every '-' or '+' in the beginning is ignored. It will match only node + * names. + * + * @param userAccessLevel + * @param fullPermissionName + * @return PermissionCheckResult.Type + */ + public PermissionCheckResult.Type comparePermissionString(String userAccessLevel, String fullPermissionName) { + + int userAccessLevelLength; + if (userAccessLevel == null || fullPermissionName == null || fullPermissionName.length() == 0 || (userAccessLevelLength = userAccessLevel.length()) == 0) { + return PermissionCheckResult.Type.NOTFOUND; + } + + PermissionCheckResult.Type result = PermissionCheckResult.Type.FOUND; + int userAccessLevelOffset = 0; + if (userAccessLevel.charAt(0) == '+') { + userAccessLevelOffset = 1; + result = PermissionCheckResult.Type.EXCEPTION; + } else if (userAccessLevel.charAt(0) == '-') { + userAccessLevelOffset = 1; + result = PermissionCheckResult.Type.NEGATION; + } + + if (fullPermissionName.equals(userAccessLevel)) { + return result; + } + + if ("groupmanager.noofflineperms".equals(fullPermissionName)) { + result = PermissionCheckResult.Type.NOTFOUND; + } + + if ("*".regionMatches(0, userAccessLevel, userAccessLevelOffset, userAccessLevelLength - userAccessLevelOffset)) { + return result; + } + int fullPermissionNameOffset; + if (fullPermissionName.charAt(0) == '+' || fullPermissionName.charAt(0) == '-') { + fullPermissionNameOffset = 1; + } else { + fullPermissionNameOffset = 0; + } + + if (userAccessLevel.charAt(userAccessLevel.length() - 1) == '*') { + return userAccessLevel.regionMatches(true, userAccessLevelOffset, fullPermissionName, fullPermissionNameOffset, userAccessLevelLength - userAccessLevelOffset - 1) ? result : PermissionCheckResult.Type.NOTFOUND; + } else { + return userAccessLevel.regionMatches(true, + userAccessLevelOffset, + fullPermissionName, + fullPermissionNameOffset, + Math.max(userAccessLevelLength - userAccessLevelOffset, fullPermissionName.length() - fullPermissionNameOffset)) ? result : PermissionCheckResult.Type.NOTFOUND; + } + } + + /** + * Returns a list of all groups. + * + * Including subgroups. + * + * @param userName + * @return String[] of all group names. + */ + @Override + public String[] getGroups(String userName) { + + ArrayList allGroups = listAllGroupsInherited(ph.getUser(userName).getGroup()); + for (Group subg : ph.getUser(userName).subGroupListCopy()) { + allGroups.addAll(listAllGroupsInherited(subg)); + } + + String[] arr = new String[allGroups.size()]; + return allGroups.toArray(arr); + } + + /** + * A Breadth-first search thru inheritance model. + * + * Just a model to copy and paste. This will guarantee the closer groups + * will be checked first. + * + * @param start + * @param targerPermission + * @return + */ + @SuppressWarnings("unused") + private Group breadthFirstSearch(Group start, String targerPermission) { + + if (start == null || targerPermission == null) { + return null; + } + LinkedList stack = new LinkedList(); + ArrayList alreadyVisited = new ArrayList(); + stack.push(start); + alreadyVisited.add(start); + while (!stack.isEmpty()) { + Group now = stack.pop(); + PermissionCheckResult resultNow = checkGroupOnlyPermission(now, targerPermission); + if (resultNow.resultType.equals(PermissionCheckResult.Type.EXCEPTION) || resultNow.resultType.equals(PermissionCheckResult.Type.FOUND)) { + return now; + } + if (resultNow.resultType.equals(PermissionCheckResult.Type.NEGATION)) { + return null; + } + for (String sonName : now.getInherits()) { + Group son = ph.getGroup(sonName); + if (son != null && !alreadyVisited.contains(son)) { + stack.push(son); + alreadyVisited.add(son); + } + } + } + return null; + } + + @Override + public Group getDefaultGroup() { + + return ph.getDefaultGroup(); + } + + @Override + public String getInfoString(String entryName, String path, boolean isGroup) { + + if (isGroup) { + Group data = ph.getGroup(entryName); + if (data == null) { + return null; + } + return data.getVariables().getVarString(path); + } else { + User data = ph.getUser(entryName); + if (data == null) { + return null; + } + return data.getVariables().getVarString(path); + } + } + + @Override + public int getInfoInteger(String entryName, String path, boolean isGroup) { + + if (isGroup) { + Group data = ph.getGroup(entryName); + if (data == null) { + return -1; + } + return data.getVariables().getVarInteger(path); + } else { + User data = ph.getUser(entryName); + if (data == null) { + return -1; + } + return data.getVariables().getVarInteger(path); + } + } + + @Override + public double getInfoDouble(String entryName, String path, boolean isGroup) { + + if (isGroup) { + Group data = ph.getGroup(entryName); + if (data == null) { + return -1; + } + return data.getVariables().getVarDouble(path); + } else { + User data = ph.getUser(entryName); + if (data == null) { + return -1; + } + return data.getVariables().getVarDouble(path); + } + + } + + @Override + public boolean getInfoBoolean(String entryName, String path, boolean isGroup) { + + if (isGroup) { + Group data = ph.getGroup(entryName); + if (data == null) { + return false; + } + return data.getVariables().getVarBoolean(path); + } else { + User data = ph.getUser(entryName); + if (data == null) { + return false; + } + return data.getVariables().getVarBoolean(path); + } + } + + @Override + public void addUserInfo(String name, String path, Object data) { + + ph.getUser(name).getVariables().addVar(path, data); + } + + @Override + public void removeUserInfo(String name, String path) { + + ph.getUser(name).getVariables().removeVar(path); + } + + @Override + public void addGroupInfo(String name, String path, Object data) { + + ph.getGroup(name).getVariables().addVar(path, data); + } + + @Override + public void removeGroupInfo(String name, String path) { + + ph.getGroup(name).getVariables().removeVar(path); + } +} diff --git a/src/main/java/org/anjocaido/groupmanager/permissions/BukkitPermissions.java b/src/main/java/org/anjocaido/groupmanager/permissions/BukkitPermissions.java new file mode 100644 index 0000000..3d2d62d --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/permissions/BukkitPermissions.java @@ -0,0 +1,497 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +package org.anjocaido.groupmanager.permissions; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; + +import org.anjocaido.groupmanager.GroupManager; +import org.anjocaido.groupmanager.data.User; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerChangedWorldEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.server.PluginDisableEvent; +import org.bukkit.event.server.PluginEnableEvent; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionAttachment; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.plugin.PluginManager; + +/** + * + * BukkitPermissions overrides to force GM reponses to Superperms + * + * @author ElgarL + */ +public class BukkitPermissions { + + protected WeakHashMap attachments = new WeakHashMap(); + protected LinkedHashMap registeredPermissions = new LinkedHashMap(); + protected GroupManager plugin; + protected boolean dumpAllPermissions = true; + protected boolean dumpMatchedPermissions = true; + private boolean player_join = false; + + /** + * @return the player_join + */ + public boolean isPlayer_join() { + + return player_join; + } + + /** + * @param player_join + * the player_join to set + */ + public void setPlayer_join(boolean player_join) { + + this.player_join = player_join; + } + + private static Field permissions; + + // Setup reflection (Thanks to Codename_B for the reflection source) + static { + try { + permissions = PermissionAttachment.class.getDeclaredField("permissions"); + permissions.setAccessible(true); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } + } + + public BukkitPermissions(GroupManager plugin) { + + this.plugin = plugin; + this.reset(); + this.registerEvents(); + + GroupManager.logger.info("Superperms support enabled."); + } + + public void reset() { + + /* + * collect new permissions + * and register all attachments. + */ + this.collectPermissions(); + this.updateAllPlayers(); + } + + private void registerEvents() { + + PluginManager manager = plugin.getServer().getPluginManager(); + + manager.registerEvents(new PlayerEvents(), plugin); + manager.registerEvents(new BukkitEvents(), plugin); + } + + public void collectPermissions() { + + registeredPermissions.clear(); + + for (Permission perm : Bukkit.getPluginManager().getPermissions()) { + registeredPermissions.put(perm.getName().toLowerCase(), perm); + } + + } + + public void updatePermissions(Player player) { + + this.updatePermissions(player, null); + } + + /** + * Push all permissions which are registered with GM for this player, on + * this world to Bukkit and make it update for the child nodes. + * + * @param player + * @param world + */ + public void updatePermissions(Player player, String world) { + + if (player == null || !GroupManager.isLoaded()) { + return; + } + + String name = player.getName(); + + // Reset the User objects player reference. + User user = plugin.getWorldsHolder().getWorldData(player.getWorld().getName()).getUser(name); + if (user != null) + user.updatePlayer(player); + + PermissionAttachment attachment; + + // Find the players current attachment, or add a new one. + if (this.attachments.containsKey(name)) { + attachment = this.attachments.get(name); + } else { + attachment = player.addAttachment(plugin); + this.attachments.put(name, attachment); + } + + if (world == null) { + world = player.getWorld().getName(); + } + + // Add all permissions for this player (GM only) + // child nodes will be calculated by Bukkit. + List playerPermArray = new ArrayList(plugin.getWorldsHolder().getWorldData(world).getPermissionsHandler().getAllPlayersPermissions(name, false)); + LinkedHashMap newPerms = new LinkedHashMap(); + + // Sort the perm list by parent/child, so it will push to superperms + // correctly. + playerPermArray = sort(playerPermArray); + + Boolean value = false; + for (String permission : playerPermArray) { + value = (!permission.startsWith("-")); + newPerms.put((value ? permission : permission.substring(1)), value); + } + + /* + * Do not push any perms to bukkit if... + * We are in offline mode + * and the player has the 'groupmanager.noofflineperms' permission. + */ + if (!Bukkit.getServer().getOnlineMode() && (newPerms.containsKey("groupmanager.noofflineperms") && (newPerms.get("groupmanager.noofflineperms") == true))) { + removeAttachment(name); + return; + } + + /** + * This is put in place until such a time as Bukkit pull 466 is + * implemented https://github.com/Bukkit/Bukkit/pull/466 + */ + try { // Codename_B source + synchronized (attachment.getPermissible()) { + + @SuppressWarnings("unchecked") + Map orig = (Map) permissions.get(attachment); + // Clear the map (faster than removing the attachment and + // recalculating) + orig.clear(); + // Then whack our map into there + orig.putAll(newPerms); + // That's all folks! + attachment.getPermissible().recalculatePermissions(); + + } + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + GroupManager.logger.finest("Attachment updated for: " + name); + } + + /** + * Sort a permission node list by parent/child + * + * @param permList + * @return List sorted for priority + */ + private List sort(List permList) { + + List result = new ArrayList(); + + for (String key : permList) { + /* + * Ignore stupid plugins which add empty permission nodes. + */ + if (!key.isEmpty()) { + String a = key.charAt(0) == '-' ? key.substring(1) : key; + Map allchildren = GroupManager.BukkitPermissions.getAllChildren(a, new HashSet()); + if (allchildren != null) { + + ListIterator itr = result.listIterator(); + + while (itr.hasNext()) { + String node = (String) itr.next(); + String b = node.charAt(0) == '-' ? node.substring(1) : node; + + // Insert the parent node before the child + if (allchildren.containsKey(b)) { + itr.set(key); + itr.add(node); + break; + } + } + } + if (!result.contains(key)) + result.add(key); + } + } + + return result; + } + + /** + * Fetch all permissions which are registered with superperms. + * {can include child nodes) + * + * @param includeChildren + * @return List of all permission nodes + */ + public List getAllRegisteredPermissions(boolean includeChildren) { + + List perms = new ArrayList(); + + for (String key : registeredPermissions.keySet()) { + if (!perms.contains(key)) { + perms.add(key); + + if (includeChildren) { + Map children = getAllChildren(key, new HashSet()); + if (children != null) { + for (String node : children.keySet()) + if (!perms.contains(node)) + perms.add(node); + } + } + } + + } + return perms; + } + + /** + * Returns a map of ALL child permissions registered with bukkit + * null is empty + * + * @param node + * @param playerPermArray + * current list of perms to check against for + * negations + * @return Map of child permissions + */ + public Map getAllChildren(String node, Set playerPermArray) { + + LinkedList stack = new LinkedList(); + Map alreadyVisited = new HashMap(); + stack.push(node); + alreadyVisited.put(node, true); + + while (!stack.isEmpty()) { + String now = stack.pop(); + + Map children = getChildren(now); + + if ((children != null) && (!playerPermArray.contains("-" + now))) { + for (String childName : children.keySet()) { + if (!alreadyVisited.containsKey(childName)) { + stack.push(childName); + alreadyVisited.put(childName, children.get(childName)); + } + } + } + } + alreadyVisited.remove(node); + if (!alreadyVisited.isEmpty()) + return alreadyVisited; + + return null; + } + + /** + * Returns a map of the child permissions (1 node deep) as registered with + * Bukkit. + * null is empty + * + * @param node + * @return Map of child permissions + */ + public Map getChildren(String node) { + + Permission perm = registeredPermissions.get(node.toLowerCase()); + if (perm == null) + return null; + + return perm.getChildren(); + + } + + /** + * List all effective permissions for this player. + * + * @param player + * @return List of permissions + */ + public List listPerms(Player player) { + + List perms = new ArrayList(); + + /* + * // All permissions registered with Bukkit for this player + * PermissionAttachment attachment = this.attachments.get(player); + * + * // List perms for this player perms.add("Attachment Permissions:"); + * for(Map.Entry entry : + * attachment.getPermissions().entrySet()){ perms.add(" " + + * entry.getKey() + " = " + entry.getValue()); } + */ + + perms.add("Effective Permissions:"); + for (PermissionAttachmentInfo info : player.getEffectivePermissions()) { + if (info.getValue() == true) + perms.add(" " + info.getPermission() + " = " + info.getValue()); + } + return perms; + } + + /** + * force Bukkit to update every OnlinePlayers permissions. + */ + public void updateAllPlayers() { + + for (Player player : Bukkit.getServer().getOnlinePlayers()) { + updatePermissions(player); + } + } + + /** + * force Bukkit to update this Players permissions. + */ + public void updatePlayer(Player player) { + + if (player != null) + this.updatePermissions(player, null); + } + + /** + * Force remove any attachments + * + * @param player + */ + private void removeAttachment(String playerName) { + + if (attachments.containsKey(playerName)) { + attachments.get(playerName).remove(); + attachments.remove(playerName); + } + } + + /** + * Remove all attachments in case of a restart or reload. + */ + public void removeAllAttachments() { + + /* + * Remove all attachments. + */ + for (String key : attachments.keySet()) { + attachments.get(key).remove(); + } + attachments.clear(); + } + + /** + * Player events tracked to cause Superperms updates + * + * @author ElgarL + * + */ + protected class PlayerEvents implements Listener { + + @EventHandler(priority = EventPriority.LOWEST) + public void onPlayerJoin(PlayerJoinEvent event) { + + setPlayer_join(true); + Player player = event.getPlayer(); + + GroupManager.logger.finest("Player Join event: " + player.getName()); + + /* + * Tidy up any lose ends + */ + removeAttachment(player.getName()); + + // force GM to create the player if they are not already listed. + plugin.getWorldsHolder().getWorldData(player.getWorld().getName()).getUser(player.getUniqueId().toString(), player.getName()); + + setPlayer_join(false); + updatePermissions(event.getPlayer()); + + setPlayer_join(false); + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onPlayerChangeWorld(PlayerChangedWorldEvent event) { // has changed worlds + + updatePermissions(event.getPlayer(), event.getPlayer().getWorld().getName()); + } + + /* + * Trigger at highest so we tidy up last. + */ + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerQuit(PlayerQuitEvent event) { + + if (!GroupManager.isLoaded()) + return; + + Player player = event.getPlayer(); + + /* + * force remove any attachments as bukkit may not + */ + removeAttachment(player.getName()); + } + } + + protected class BukkitEvents implements Listener { + + @EventHandler(priority = EventPriority.NORMAL) + public void onPluginEnable(PluginEnableEvent event) { + + if (!GroupManager.isLoaded()) + return; + + collectPermissions(); + updateAllPlayers(); + } + + @EventHandler(priority = EventPriority.NORMAL) + public void onPluginDisable(PluginDisableEvent event) { + + collectPermissions(); + // updateAllPlayers(); + } + } + +} diff --git a/src/main/java/org/anjocaido/groupmanager/permissions/PermissionsReaderInterface.java b/src/main/java/org/anjocaido/groupmanager/permissions/PermissionsReaderInterface.java new file mode 100644 index 0000000..c9fea34 --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/permissions/PermissionsReaderInterface.java @@ -0,0 +1,255 @@ +package org.anjocaido.groupmanager.permissions; + +// import java.util.Collection; +// import java.util.Map; +// import java.util.Set; +import java.util.List; +import java.util.Set; + +import org.anjocaido.groupmanager.data.Group; +// import org.anjocaido.groupmanager.data.User; +import org.bukkit.entity.Player; + +/** + * Made by Nijikokun. Changed by Gabriel Couto + * + * This class is intended to *read* permissions from a single world. + * + * @author Nijikokun + * @author Gabriel Couto + * @author ElgarL + */ +public abstract class PermissionsReaderInterface { + + /** + * + * @param player + * @param string + * @return true if has permission + */ + public abstract boolean has(Player player, String string); + + /** + * + * @param player + * @param string + * @return true if has permission + */ + public abstract boolean permission(Player player, String string); + + /** + * + * @param userName + * @return group name for this player. + */ + public abstract String getGroup(String userName); + + /** + * + * @param userName + * @param groupName + * @return true if in group + */ + public abstract boolean inGroup(String userName, String groupName); + + /** + * + * @param groupName + * @return String of prefix + */ + public abstract String getGroupPrefix(String groupName); + + /** + * + * @param groupName + * @return String of suffix + */ + public abstract String getGroupSuffix(String groupName); + + /** + * + * @param groupName + * @return true if can build + */ + public abstract boolean canGroupBuild(String groupName); + + /** + * + * @param groupName + * @param node + * @return String value + */ + public abstract String getGroupPermissionString(String groupName, String node); + + /** + * + * @param groupName + * @param node + * @return integer value + */ + public abstract int getGroupPermissionInteger(String groupName, String node); + + /** + * + * @param groupName + * @param node + * @return boolean value + */ + public abstract boolean getGroupPermissionBoolean(String groupName, String node); + + /** + * + * @param groupName + * @param node + * @return double value + */ + public abstract double getGroupPermissionDouble(String groupName, String node); + + /** + * + * @param userName + * @param node + * @return String value + */ + public abstract String getUserPermissionString(String userName, String node); + + /** + * + * @param userName + * @param node + * @return integer value + */ + public abstract int getUserPermissionInteger(String userName, String node); + + /** + * + * @param userName + * @param node + * @return boolean value + */ + public abstract boolean getUserPermissionBoolean(String userName, String node); + + /** + * + * @param userName + * @param node + * @return double value + */ + public abstract double getUserPermissionDouble(String userName, String node); + + /** + * + * @param userName + * @param node + * @return String value + */ + public abstract String getPermissionString(String userName, String node); + + /** + * + * @param userName + * @param node + * @return integer value + */ + public abstract int getPermissionInteger(String userName, String node); + + /** + * + * @param userName + * @param node + * @return boolean value + */ + public abstract boolean getPermissionBoolean(String userName, String node); + + /** + * + * @param userName + * @param node + * @return double value + */ + public abstract double getPermissionDouble(String userName, String node); + + ///////////////////////////// + /** + * Gets the appropriate prefix for the user. + * This method is a utility method for chat plugins to get the user's prefix + * without having to look at every one of the user's ancestors. + * Returns an empty string if user has no parent groups. + * + * @param user + * Player's name + * @return Player's prefix + */ + public abstract String getUserPrefix(String user); + + /** + * Gets the appropriate suffix for the user. + * This method is a utility method for chat plugins to get the user's suffix + * without having to look at every one of the user's ancestors. + * Returns an empty string if user has no parent groups. + * + * @param user + * Player's name + * @return Player's suffix + */ + public abstract String getUserSuffix(String user); + + /** + * Returns the group object representing the default group of the given + * world. + * This method will return null if the object does not exist or the world + * has no default group. + * + * @return Group object representing default world, or null if it doesn't + * exist or is not defined. + */ + public abstract Group getDefaultGroup(); + + /** + * Gets a array of the names of all parent groups in the same world. + * + * @param name + * Target user's name + * @return An array containing the names of all parent groups (including + * ancestors) that are in the same world + */ + public abstract String[] getGroups(String name); + + public abstract String getInfoString(String entryName, String path, boolean isGroup); + + // public abstract String getInfoString(String entryName, String path, boolean isGroup, Comparator comparator); + + public abstract int getInfoInteger(String entryName, String path, boolean isGroup); + + // public abstract int getInfoInteger(String entryName, String path, boolean isGroup, Comparator comparator); + + /** + * Gets a double from the Info node without inheritance. + * + * @param entryName + * @param path + * @param isGroup + * @return -1 if not found + */ + public abstract double getInfoDouble(String entryName, String path, boolean isGroup); + + // public abstract double getInfoDouble(String entryName, String path, boolean isGroup, Comparator comparator); + + public abstract boolean getInfoBoolean(String entryName, String path, boolean isGroup); + + // public abstract boolean getInfoBoolean(String entryName, String path, boolean isGroup, Comparator comparator); + + public abstract void addUserInfo(String name, String path, Object data); + + public abstract void removeUserInfo(String name, String path); + + public abstract void addGroupInfo(String name, String path, Object data); + + public abstract void removeGroupInfo(String name, String path); + + ////////////////////////////// + + public abstract List getAllPlayersPermissions(String userName); + + public abstract Set getAllPlayersPermissions(String userName, Boolean includeChildren); +} diff --git a/src/main/java/org/anjocaido/groupmanager/utils/GMLoggerHandler.java b/src/main/java/org/anjocaido/groupmanager/utils/GMLoggerHandler.java new file mode 100644 index 0000000..cefaa52 --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/utils/GMLoggerHandler.java @@ -0,0 +1,27 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.anjocaido.groupmanager.utils; + +import java.util.logging.ConsoleHandler; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +/** + * + * @author gabrielcouto + */ +public class GMLoggerHandler extends ConsoleHandler { + + @Override + public void publish(LogRecord record) { + + String message = "GroupManager - " + record.getLevel() + " - " + record.getMessage(); + if (record.getLevel().equals(Level.SEVERE) || record.getLevel().equals(Level.WARNING)) { + System.err.println(message); + } else { + System.out.println(message); + } + } +} diff --git a/src/main/java/org/anjocaido/groupmanager/utils/GroupManagerPermissions.java b/src/main/java/org/anjocaido/groupmanager/utils/GroupManagerPermissions.java new file mode 100644 index 0000000..ff98f25 --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/utils/GroupManagerPermissions.java @@ -0,0 +1,56 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.anjocaido.groupmanager.utils; + +/** + * Just a list of commands for this plugin + * + * @author gabrielcouto + */ +public enum GroupManagerPermissions { + + manuadd, + manudel, + manuaddsub, + manudelsub, + mangadd, + mangdel, + manuaddp, + manudelp, + manuclearp, + manulistp, + manucheckp, + mangaddp, + mangdelp, + mangclearp, + manglistp, + mangcheckp, + mangaddi, + mangdeli, + manuaddv, + manudelv, + manulistv, + manucheckv, + mangaddv, + mangdelv, + manglistv, + mangcheckv, + manwhois, + tempadd, + tempdel, + templist, + tempdelall, + mansave, + manload, + listgroups, + manpromote, + mandemote, + mantogglevalidate, + mantogglesave, + manworld, + manselect, + manclear, + mancheckw +} diff --git a/src/main/java/org/anjocaido/groupmanager/utils/PermissionCheckResult.java b/src/main/java/org/anjocaido/groupmanager/utils/PermissionCheckResult.java new file mode 100644 index 0000000..37d6a67 --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/utils/PermissionCheckResult.java @@ -0,0 +1,67 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.anjocaido.groupmanager.utils; + +import org.anjocaido.groupmanager.data.DataUnit; + +/** + * + * @author gabrielcouto + */ +public class PermissionCheckResult { + + /** + * It should be the owner of the access level found. + * + * Use instanceof to find the owner type + */ + public DataUnit owner; + /** + * The permission node found in the DataUnit. + */ + public String accessLevel; + /** + * The full name of the permission you are looking for + */ + public String askedPermission; + /** + * The result conclusion of the search. + * It determines if the owner can do, or not. + * + * It even determines if it has an owner. + */ + public Type resultType = Type.NOTFOUND; + + /** + * The type of result the search can give. + */ + public enum Type { + + /** + * If found a matching node starting with '+'. + * It means the user CAN do the permission. + */ + EXCEPTION, + /** + * If found a matching node starting with '-'. + * It means the user CANNOT do the permission. + */ + NEGATION, + /** + * If just found a common matching node. + * IT means the user CAN do the permission. + */ + FOUND, + /** + * If no matchin node was found. + * It means the user CANNOT do the permission. + * + * owner field and accessLevel field should not be considered, + * when type is + * NOTFOUND + */ + NOTFOUND + } +} diff --git a/src/main/java/org/anjocaido/groupmanager/utils/StringPermissionComparator.java b/src/main/java/org/anjocaido/groupmanager/utils/StringPermissionComparator.java new file mode 100644 index 0000000..1fdaaee --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/utils/StringPermissionComparator.java @@ -0,0 +1,52 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.anjocaido.groupmanager.utils; + +import java.util.Comparator; + +/** + * + * @author gabrielcouto + */ +public class StringPermissionComparator implements Comparator { + + @Override + public int compare(String permA, String permB) { + + boolean ap = permA.startsWith("+"); + boolean bp = permB.startsWith("+"); + boolean am = permA.startsWith("-"); + boolean bm = permB.startsWith("-"); + if (ap && bp) { + return 0; + } + if (ap && !bp) { + return -1; + } + if (!ap && bp) { + return 1; + } + if (am && bm) { + return 0; + } + if (am && !bm) { + return -1; + } + if (!am && bm) { + return 1; + } + return permA.compareToIgnoreCase(permB); + } + + private static StringPermissionComparator instance; + + public static StringPermissionComparator getInstance() { + + if (instance == null) { + instance = new StringPermissionComparator(); + } + return instance; + } +} diff --git a/src/main/java/org/anjocaido/groupmanager/utils/Tasks.java b/src/main/java/org/anjocaido/groupmanager/utils/Tasks.java new file mode 100644 index 0000000..7c11d1c --- /dev/null +++ b/src/main/java/org/anjocaido/groupmanager/utils/Tasks.java @@ -0,0 +1,175 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.anjocaido.groupmanager.utils; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.List; + +import org.anjocaido.groupmanager.GroupManager; +import org.anjocaido.groupmanager.data.Group; + +/** + * + * @author gabrielcouto + */ +public abstract class Tasks { + + /** + * Gets the exception stack trace as a string. + * + * @param exception + * @return stack trace as a string + */ + public static String getStackTraceAsString(Exception exception) { + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + exception.printStackTrace(pw); + return sw.toString(); + } + + public static void copy(InputStream src, File dst) throws IOException { + + InputStream in = src; + OutputStream out = new FileOutputStream(dst); + + // Transfer bytes from in to out + byte[] buf = new byte[1024]; + int len; + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + out.close(); + try { + in.close(); + } catch (Exception e) { + } + } + + public static void copy(File src, File dst) throws IOException { + + InputStream in = new FileInputStream(src); + copy(in, dst); + } + + /** + * Appends a string to a file + * + * @param data + * @param file + */ + public static void appendStringToFile(String data, String file) throws IOException { + + FileWriter outStream = new FileWriter("." + System.getProperty("file.separator") + file, true); + + BufferedWriter out = new BufferedWriter(outStream); + + data.replaceAll("\n", System.getProperty("line.separator")); + + out.append(new SimpleDateFormat("yyyy-MM-dd HH-mm").format(System.currentTimeMillis())); + out.append(System.getProperty("line.separator")); + out.append(data); + out.append(System.getProperty("line.separator")); + + out.close(); + } + + public static void removeOldFiles(GroupManager gm, File folder) { + + if (folder.isDirectory()) { + long oldTime = System.currentTimeMillis() - (((long) gm.getGMConfig().getBackupDuration() * 60 * 60) * 1000); + for (File olds : folder.listFiles()) { + if (olds.isFile()) { + if (olds.lastModified() < oldTime) { + try { + olds.delete(); + } catch (Exception e) { + } + } + } + } + } + } + + public static String getDateString() { + + GregorianCalendar now = new GregorianCalendar(); + String date = ""; + date += now.get(Calendar.DAY_OF_MONTH); + date += "-"; + date += now.get(Calendar.HOUR); + date += "-"; + date += now.get(Calendar.MINUTE); + return date; + } + + public static String getStringListInString(List list) { + + if (list == null) { + return ""; + } + String result = ""; + for (int i = 0; i < list.size(); i++) { + result += list.get(i); + if (i < list.size() - 1) { + result += ", "; + } + } + return result; + } + + public static String getStringArrayInString(String[] list) { + + if (list == null) { + return ""; + } + String result = ""; + for (int i = 0; i < list.length; i++) { + result += list[i]; + if (i < ((list.length) - 1)) { + result += ", "; + } + } + return result; + } + + public static String getGroupListInString(List list) { + + if (list == null) { + return ""; + } + String result = ""; + for (int i = 0; i < list.size(); i++) { + result += list.get(i).getName(); + if (i < list.size() - 1) { + result += ", "; + } + } + return result; + } + + public static String join(String[] arr, String separator) { + + if (arr.length == 0) + return ""; + String out = arr[0].toString(); + for (int i = 1; i < arr.length; i++) + out += separator + arr[i]; + return out; + } + +} diff --git a/src/main/resources/Changelog.txt b/src/main/resources/Changelog.txt new file mode 100644 index 0000000..50a947d --- /dev/null +++ b/src/main/resources/Changelog.txt @@ -0,0 +1,233 @@ +Changelog + +v 1.1: + - Fixed users being able to use 'manuadd' to add users to higher groups than their own. + - Added SuperPerms support so GM will update and provide the permissions to plugins which only support Bukkit Perms. + - Added more helpful output to errors on argument lengths. + - GroupManager will now attempt to select the default world when using commands instead of failing and telling you to use '/manselect '. + - Invalid groups assigned to players in users.yml will no longer cause a crash. GM will now set them to the default group instead. + - Fix for Users.yml containing only 'users:' causing a crash. + - GroupManager will now generate a fresh Users and Groups yml if either file is empty. + - Fix for an infinite loop bug with the new Bukkit Perms during a new user creation. + - Fixed BukkitPerms population. Wasn't correctly setting superperms. + - Push updates to superperms for all valid GM commands. + - All GroupManager commands issued by players are now echoed in the console. + - Reverted WorldHolder static change to maintain backward plugin compatibility. + - Update to handle 'getDescription().getPermissions(') returning a list (CB 1172). + - Fix for null in PLAYER_TELEPORT for bukkit perms. + - Fixed wasteful updating of perms on a manload. + - manulistp now accepts an additional + to list ALL Superperms effective permissions (/manulistp +). + - manucheckp also outputs superperms results. + - Removed superperms update on plugins unloading. Unneeded and created undesired lag on shutdown. + - Added a BukkitPermsUpdateTask to only update superperms once on a load/reload. + - Fix for GM not checking inheritance for known superperms nodes. + - Optimized getAllPlayersPermissions and fixed pushing unknown perms to superperms. +v 1.2: + - Changed priority of Registered events to lowest. + - Fixed an issue with superperms where plugins define perms with inheritance after the root perms +v 1.3: + - Rewrote Config loading to use Bukkits Configuration features + - Added an opOverride setting in config. + If present and set to false, op's will not get overriding permissions in GroupManager. + (one op will not be able to alter another op's settings) + - GM will now create all relevant world data files for non mirrored worlds. + (for all worlds named in config.yml) + - Attempt to stop GM wiping groups/users yml's on a bad shut down. + - Added event handling to manage new world creation at runtime. + - Added the ability to handle unknown worlds at server start. + (GM will create the data files for any worlds it finds which are not in the config.yml) + - Fix for Bukkit passing a null To location on a player Portaling + - Fixed manudelsub not correctly selecting the group to remove. + - Added two new permission nodes - groupmanager.notify.self & groupmanager.notify.other + These allow players/admins to be notified when players are moved between groups. +v 1.4: + - Updated for Bukkits new YamlConfiguration. + - Cleared remaining Cast errors cause by object cloning. + - Removed extra notification messages for the player issuing the group move command. + - Added a config setting - bukkit_perms_override: false + Enable to allow default Bukkit based permissions to remain enabled, unless directly negated within GroupManager. + - Fixed reading world mirrors from the config. + - Simplified config.yml while retaining backwards compatibility. + - Added data.save.hours setting to config. This allow control over how long backups are retained. +v 1.5: + - Fixed opOverrides and bukkit_perms_override to read the correct entries. + - Better commenting in config.yml + - Fixed GM to recognize Superperm child nodes. + If you add a node like Towny.admin GM will now correctly report on all child nodes. + - Fixed GM loading world data files twice at startup. + - Improved error reporting for invalid groups.yml + - Added Global Groups + Defined in groupmanager/globalgroups.yml. + Create groups in the yml with a g: prefix, then inherit in the worlds groups files. + - Added Info node support to Global Groups. + - Fixed an error on 'manucheckv'. If the users doesn't have the variable it fell through causing an exception. + - Added checking of subgroups for Info nodes. + - Expanded 'canUserBuild()' to include inheritance and subgroups. + - Added a config.yml setting of 'validate_toggle' for those who prefer 'mantogglevalidate' to always be off. + - Prevent setting 'minutes' in the config to zero causing an error. + - GM will now check to see if it's data files have been changed at each scheduled save. + If the files have been altered (on disc) it will reload, so long as the in-memory data hasn't changed. + If the files on Disc have changed AND there have been changes to it's in-memory data it will show a warning. + You then MUST issue a '/mansave force' to overwrite the disc files, or a '/manload' to overwrite the memory data. + - Fix for an error in checkFullUserPermission caused by players disconnecting mid perms update. + - Notification of being moved to the default group only happens if it's a demotion/promotion (not on join). + - Fixed GM holding files open and causing the time stamp to be incorrect. This caused GM to require a '/mansave force' when it shouldn't be needed. + - Fixed a crash on reload due to bukkit not unloading plugins before reloading. +v 1.6: + - Prevent Group.equals tests throwing a NullPointerException for GlobalGroups. + - Stop throwing errors on an empty users file. + - Optimize sorting to speedup permission tests. + - Fix superperms to pass all tests http://dev.bukkit.org/server-mods/superpermstest/ + - Optimizations include changing the return of comparePermissionString. + - Added file details in error messages for loading groups/users. +v 1.7: + - GM now supports offline players without having to mantogglevalidate + - Offline player checks now support partial name matches. + - Added custom events so plugins can now be notified of changes within GroupManager. + - GM now registers with Bukkits ServicesManager. + - deleting the contents of GlobalGroups.yml will no longer thrown a NullPointerException. + - Removed op permissions from admins in the default GloblaGroups.yml. +v 1.8: + - Changed ServicesManager registration to lowest from normal. + - Fixed 'manucheckp' returning a null for the searched node when it's a group/subgroup. + - 'manpromote' and 'mandemote' now correctly send the notification to the console if the command was issued there. + - Expanded GlobalGroups.yml and Groups.yml to include Towny permissions. + - Delayed GroupManager events so Superperms will be fully updated before plugins receive the events. + - Changed the way events are raised to prevent variable corruption. + - Reload GlobalGroups when you perform a world load. + - Changed GlobalGroups to save/load before local groups in the scheduled data saving/loading + - Fix 'manucheckp' to correctly report if a permission is available from GroupManager or Bukkit. + - Changed over to a reflection method for populating superperms as Bukkit lags when you handle permissions one at a time. + - Major, MAJOR changes to support partial/full world mirroring. + You can now mirror groups.yml, users.yml or both files between different worlds. + - Catch NullPointerErrors generated by blank permission nodes. + - Removed '- bukkit.command' form the globalgroups permission nodes. +v 1.9: + - Optimize populating Bukkit perms so we no longer calculate the child nodes (Bukkit already does this). + - Added a tidy error message for invalid permission entries in GlobalGroups. + - Better optimize assembling of a players permissions and allow the * node to populate all registered superperms. + - Fixed text when adding a subgroup to not say the player was moved. + - Update to new Bukkit Event system. + - Update GroupManagerBridge for new event system. + - Fixed a random null error upon a player portaling. + - Fixed infinite loop error on player join. + - Optimized code to only update the player logging in instead of all players online. + - Added recursive loop detection for World mirroring (you may not set the main world as a mirror of another). + - Fixed fetching world data so it no longer returns the mirrored world for groups. Each world data holder now points to the correct data set, so can be returned as an object. + - Changed addSubGroup() to only add the group if it doesn't already exist (no need to update an already existing group). + - addSubGroup now returns a boolean for success/failure. + - '/manuaddsub' now correctly reports if it was able to add the sub group. + - Allow negation to the * permission node when populating superperms. + - Fix trying to modify an unmodifiable collection breaking superperms. + - Fixed subgroups (I broke earlier). + - Check for a null player object in the PlayerTeleportEvent. + - Trap errors in fetching the mirrors map. + - Fixed an infinite loop error when using '/manudel' on a logged in player. It caused setDefaultGroup to trigger a bukkit update when no GM User existed yet. + - do not allow inherited permissions to negate higher perms. + - Fixed a bug when pushing superperms in the wrong order. + - Fix players retaining permissions when demoted. + - Auto sort permissions on load to speed up population of superperms. + - Negating a parent node after adding all nodes with * will now correctly remove all child nodes of that parent before populating superperms. + eg. + - '*' + - -vanish.* + - vanish.standard + - Track the 'onPlayerChangeWorld' event as some teleports seem to not be triggering a world move. + - Catch all errors in badly formatted groups. + - Fix a bug with getWorldData return the main world data for all mirrors, instead of the worlds parent data. + - Prevent getAllPlayersPermissions() processing a group more than once. Improves performance when using complex inheritance structures. + - Fix world mirroring so it correctly creates data files and data sources for partially mirrored worlds. + - Fixed world mirroring so it returns the correct data for the requested world. + - Change Service registration to register WorldsHolder instead of AnjoPermissionsHandler. This is the correct entry point for all data. + - Depreciate PlayerTeleportEvent, PlayerRespawnEvent and PlayerPortalEvent as it's all handled in PlayerChangedWorldEvent. + This also means we no longer update permissions before we change worlds. + - A command of '/manload' with no world arguments now performs a full reload of GM. + - Update for Bukkit R5 compatability. + - Removed BukkitPermsOverride as this is now the default with bukkit handling child nodes. + - Prevent adding inheritances and info nodes to globalgroups. These are permissions collections, not player groups. + - Prevent promoting players to, and demoting to GlobalGroups. + - Make 'manload' reload the config correctly. + - Minor optimization when checking bukkit permissions. + - Better reporting when a users.yml is failing to load. + - Expanded '/manuadd'to accept an optional variable for the world (eg '/manuadd '). + - Removed some debug spam. + - Don't remove an attachment on a player leaving as Bukkit never forgets it. This fixes non mirrored permissions being messed up if a player relogs. + - Treat all world names as lower case for file handling (please check in your worlds folder. You should have no folders with upper case letters from now). + - Auto rename all case sensitive world folders to lower case (if possible). + - Update GlobalGroups.yml for new/changed Towny permission nodes. + - Stop attempting to push empty permissions when players edit the yml's incorrectly. + - Catch errors caused by bad indentation in yml's. + - Force remove player attachments on disconnect, and tidyup during player join in case of any errors. Fixes a bug of losing permissions. + - Added a new permission node 'groupmanager.op'. This will cause players with this node to be treated as op's when + using GroupManager commands (they will still require each commands permission node to use them). + - Prevent Null entries in group inheritance from throwing errors. +v 2.0: + - Fix GM reporting of permission inheritance to retain the correct order. Lower inheritance groups can no longer negate a higher groups permissions. + - Fix an error I caused trying to modify an unmodifiable list when parsing '*' permissions. + - Don't throw errors when attempting to remove permission attachments (bukkit will have already removed it). + - Remove all permission attachments when performing a manload or restart. + - Expand 'manwhois' to also list a users subgroups. + - Fix a concurrent modification error when removing all attachments. + - Better handling of errors in user and group yml's. + - Added missing confirmation message on '/manload'. + - Stop the error on shutdown if GM failed to load at startup. + - GroupManager will now generate it's own log (in the GM folder) to keep things tidy, but also to account of those players unable to find/access their server.log. + - Startup errors will now lock out ALL commands other than '/manload' + - Fix 'manuadd' to use the default or selected world (via 'manselect'), if the world is not specified in the command. + - Expand GlobalGroups.yml and groups.yml to cover the VanishNoPacket plugin. Demonstrating how to negate and add nodes when using the '*' permission with inheritance. + - Fix silly nested throw/catch statements. Errors are now correctly generated when reading yml's. + - Unregister the worldsHolder as a service on a reload/shutdown instead of the whole plugin. + - Update all code formatting to use tabs for indentation. + - Stop using our own deprecated methods as we tell others to do. + - Finally remove all deprecated methods. + - Re-initialize the WorldsHolder on a reload, as un-registering and re-registering a new holder means all plugins have to check for the new service on every quiery. + - Prevent null perms getting past the GlobalGroups loader. + - Fix forgetting sub groups on a manload. + - Allow 'manucheckp' to notify when superperms reports false but it is really negated. + - Only output a Data update message if something has changed. + - Fix loading users with only numerals in their names to be seen as strings. + - Ignore any sub folders in the Worlds folder which start with a period (fix for storing data in svn respoitories). + - Throw a better error than 'null' when someone removes all groups from a yml. + - Stop force removing attachments and let Bukkit handle it's own mess. + - Change to our own Yaml parsing for globalgroups instead of using the YAMLConfiguration class in bukkit. + - Fix a cases sensitivity bug in world loading. + - Stop using the YamlConfiguration in bukkit for our config handling. We can now support periods in world names. + - Fix GlobalGroups not loading permission nodes. + - Fix an error with Logging set to 'OFF' triggering a cast exception. + - No more null errors from corrupt config.yml's. + - Give a better error when a subgroup is null. + - Include the GM version when logging errors. + - Fix Synchronization on adding subgroups (thanks snowleo). + - Remove info node support from GlobalGroups. It should not have them as GlobalGroups are only permission collections. + - Change order of data in Users.yml to [name, Group, SubGroup, Permissions, Info nodes]. + - Add alphabetically sorted user lists. + - allWorldsDataList now returns fully mirrored worlds which are not identical mirrors (fixes the /manselect list). + - Add support for Rcon. + - Prevent GM commands from being used on CommandBlocks. + - Clear our attachment map upon a manload so we correctly reconfigure a players new permissions. + - Synchronize the raising of GroupManager events to Bukkit.getServer() (should prevent deadlocks). + - Synchronize pushing to Bukkit perms to prevent any ConcurrentModificationException. + - Do not grant any permissions (nor update Bukkit) if the server is in offline mode and the player has the permission node 'groupmanager.noofflineperms'. + - Negate 'groupmanager.noofflineperms' by default in the owner group. + - Add support for BukkitForge using 'overworld' as the main world name. + - Prevent '*' permissions granting the 'groupmanager.noofflineperms' permission. + - Added '/mancheckw ' to inspect which permission files a world is referencing. + - Add config potion to set if GM commands should be allowed on CommnandBlocks. + - Catch the error when using an out of date config for 'allow_commandblocks' So it doesn't kill the whole config. + - '/manselect' will no longer list duplicate worlds. + - Added a new mirroring option in the config of 'all_unnamed_worlds'. This will cause all new or unnamed worlds to use this mirroring. + - Don't allow adding a node with '/manuaddp' and '/mangaddp' which is already negated. + - Warn when adding a node where an exception already exist. + - Only prevent adding nodes with '/manuaddp' and '/mangaddp' if they are exact matches (not wildcards). + - Store worldSelection indexed on the senders name rather than the object (fixes commandblocks using manselect). + - Check subgroup permissions with an equal priority so no one subgroup is higher ranked than another. + - add recursive permission adding/deleting + - Prevent adding sub groups for ranks the granting player doesn't have access to. + - Allow Exceptions in any inherited group to override negation of permissions. +v2.1: + - Update for CraftBukkit 1.7.8-R0.1(3050). + - Add UUID support. + Plugins can still query by player name but a UUID is faster and preferable. + - Set a default mirror map if none is found in the config. + - Fix clones forgetting sub groups. + - Prevent players who have never logged in before from taking over existing accounts. \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..44cfd62 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,50 @@ +settings: + config: + # With this enabled anyone set as op has full permissions when managing GroupManager + # The user will be able to promote players to the same group or even above. + opOverrides: true + + # Default setting for 'mantogglevalidate' + # true will cause GroupManager to attempt name matching by default. + validate_toggle: true + # ************************************************************************************************************************************************************ + # *** NOTE: Having this feature enabled can allow improper use of Command Blocks which may lead to undesireable permission changes. You have been warned! *** + # ************************************************************************************************************************************************************ + allow_commandblocks: false + + data: + save: + # How often GroupManager will save it's data back to groups.yml and users.yml + minutes: 10 + # Number of hours to retain backups (plugins/GroupManager/backup) + hours: 24 + + logging: + # Level of detail GroupManager will use when logging. + # Acceptable entries are - ALL,CONFIG,FINE,FINER,FINEST,INFO,OFF,SEVERE,WARNING + level: INFO + + mirrors: + # Worlds listed here have their settings mirrored in their children. + # The first element 'world' is the main worlds name, and is the parent world. + # subsequent elements 'world_nether' and 'world_the_end' are worlds which will use + # the same user/groups files as the parent. + # the element 'all_unnamed_worlds' specifies all worlds that aren't listed, and automatically mirrors them to it's parent. + # Each child world can be configured to mirror the 'groups', 'users' or both files from its parent. + world: + world_nether: + - users + - groups + world_the_end: + - users + - groups + all_unnamed_worlds: + - users + - groups + # world2: (World2 would have it's own set of user and groups files) + # world3: + # - users (World3 would use the users.yml from world2, but it's own groups.yml) + # world4: + # - groups (World4 would use the groups.yml from world2, but it's own users.yml) + # world5: + # - world6 (this would cause world6 to mirror both files from world5) diff --git a/src/main/resources/globalgroups.yml b/src/main/resources/globalgroups.yml new file mode 100644 index 0000000..cec4d88 --- /dev/null +++ b/src/main/resources/globalgroups.yml @@ -0,0 +1,329 @@ +# These groups only contain permission nodes. +# +# **** You can NOT add anything other than permission nodes **** +# **** This is NOT where you set up the groups which you give to users! **** +# **** goto groupmanager/worlds/worldname/groups.yml if you want to set the actual groups! **** +# +# These collections are to be inherited in your different worlds groups.yml's +# They can also be added as one of a users subgroups, but NOT as a primary group. +# These collections are available to ALL group and user yml's. +# +# Add to and customize these groups to fit your needs. + +groups: + +# Permission nodes for GroupManager +# by ElgarL, snowleo, continued from gabrielcouto's original +# http://wiki.ess3.net + + g:groupmanager_default: + permissions: + - groupmanager.notify.self + + g:groupmanager_moderator: + permissions: + - groupmanager.listgroups + - groupmanager.mandemote + - groupmanager.manpromote + - groupmanager.manselect + - groupmanager.manuadd + - groupmanager.manudel + - groupmanager.manwhois + - groupmanager.notify.other + + g:groupmanager_admin: + permissions: + - groupmanager.mantogglevalidate + - groupmanager.mansave + - groupmanager.mangcheckp + - groupmanager.manglistp + - groupmanager.manucheckp + - groupmanager.manulistp + +# Permission nodes for CraftBukkit +# by many devs and contributors +# http://dl.bukkit.org/ + + g:bukkit_default: + permissions: + - bukkit.broadcast.user + - -bukkit.command.plugins + + g:bukkit_moderator: + permissions: + - bukkit.command.ban + - bukkit.command.ban.ip + - bukkit.command.ban.player + - bukkit.command.gamemode + - bukkit.command.kick + - bukkit.command.unban + - bukkit.command.unban.ip + - bukkit.command.unban.player + + g:bukkit_admin: + permissions: + - bukkit.broadcast + - bukkit.broadcast.admin + - bukkit.command.give + - bukkit.command.help + - bukkit.command.kill + - bukkit.command.list + - bukkit.command.me + - -bukkit.command.op + - -bukkit.command.op.give + - -bukkit.command.op.take + - bukkit.command.plugins + - bukkit.command.reload + - bukkit.command.save + - bukkit.command.save.disable + - bukkit.command.save.enable + - bukkit.command.save.perform + - bukkit.command.say + - bukkit.command.stop + - bukkit.command.teleport + - bukkit.command.tell + - bukkit.command.time + - bukkit.command.time.add + - bukkit.command.time.set + - bukkit.command.version + - bukkit.command.whitelist + - bukkit.command.whitelist.add + - bukkit.command.whitelist.disable + - bukkit.command.whitelist.enable + - bukkit.command.whitelist.list + - bukkit.command.whitelist.reload + - bukkit.command.whitelist.remove + +# Permission nodes for Essentials +# by ementalo, snowleo, and KHobbits +# http://dev.bukkit.org/server-mods/essentials/ + + g:essentials_default: + permissions: + - essentials.help + - essentials.helpop + - essentials.list + - essentials.motd + - essentials.rules + - essentials.spawn + - essentials.jail.allow.help + - essentials.jail.allow.helpop + - essentials.jail.allow.rules + + g:essentials_builder: + permissions: + - essentials.afk + - essentials.afk.auto + - essentials.back + - essentials.back.ondeath + - essentials.balance + - essentials.balance.others + - essentials.balancetop + - essentials.book + - essentials.chat.color + - essentials.chat.format + - essentials.chat.shout + - essentials.chat.question + - essentials.compass + - essentials.delhome + - essentials.depth + - essentials.exp + - essentials.getpos + - essentials.hat + - essentials.home + - essentials.ignore + - essentials.itemdb + - essentials.kit + - essentials.kits.tools + - essentials.mail + - essentials.mail.send + - essentials.me + - essentials.msg + - essentials.msg.color + - essentials.msg.format + - essentials.nick + - essentials.pay + - essentials.ping + - essentials.powertool + - essentials.powertooltoggle + - essentials.protect + - essentials.recipe + - essentials.seen + - essentials.sethome + - essentials.sethome.bed + - essentials.sethome.multiple + - essentials.signs.use.* + - essentials.signs.create.disposal + - essentials.signs.create.mail + - essentials.signs.create.protection + - essentials.signs.create.trade + - essentials.signs.break.disposal + - essentials.signs.break.mail + - essentials.signs.break.protection + - essentials.signs.break.trade + - essentials.suicide + - essentials.time + - essentials.tpa + - essentials.tpaccept + - essentials.tpahere + - essentials.tpdeny + - essentials.warp + - essentials.warp.list + - essentials.worth + - essentials.jail.allow.mail + - essentials.jail.allow.ping + - essentials.jail.allow.seen + + g:essentials_moderator: + permissions: + - -essentials.spawner.enderdragon + - essentials.afk.kickexempt + - essentials.ban + - essentials.ban.notify + - essentials.banip + - essentials.book.title + - essentials.book.others + - essentials.broadcast + - essentials.chat.url + - essentials.chat.magic + - essentials.clearinventory + - essentials.delwarp + - essentials.eco.loan + - essentials.exp.others + - essentials.ext + - essentials.fly + - essentials.fly.safelogin + - essentials.getpos + - essentials.getpos.others + - essentials.helpop.receive + - essentials.home.others + - essentials.invsee + - essentials.jails + - essentials.jump + - essentials.kick + - essentials.kick.notify + - essentials.kill + - essentials.kits.* + - essentials.msg.magic + - essentials.mute + - essentials.mute.notify + - essentials.nick.color + - essentials.nick.others + - essentials.realname + - essentials.seen.banreason + - essentials.seen.extra + - essentials.setwarp + - essentials.signs.create.* + - essentials.signs.break.* + - essentials.spawner + - essentials.spawner.* + - essentials.thunder + - essentials.time + - essentials.time.set + - essentials.protect.alerts + - essentials.protect.admin + - essentials.protect.ownerinfo + - essentials.ptime + - essentials.ptime.others + - essentials.togglejail + - essentials.top + - essentials.tp + - essentials.tp.others + - essentials.tphere + - essentials.tppos + - essentials.tptoggle + - essentials.unban + - essentials.unbanip + - essentials.vanish + - essentials.vanish.effect + - essentials.warps.* + - essentials.weather + - essentials.whois + - essentials.workbench + - essentials.world + - essentials.worlds.* + - essentials.jail.allow.jails + - essentials.jail.allow.togglejail + + g:essentials_admin: + permissions: + - -essentials.backup + - -essentials.essentials + - -essentials.setspawn + - -essentials.reloadall + - -essentials.plugin + - essentials.* + +# Permission nodes for Towny by ElgarL +# http://dev.bukkit.org/server-mods/towny-advanced/ + + g:towny_default: + permissions: + - towny.chat.local + + g:towny_builder: + permissions: + - towny.wild.build.6 + - towny.wild.destroy.6 + - towny.wild.destroy.14 + - towny.wild.destroy.15 + - towny.wild.destroy.16 + - towny.wild.build.17 + - towny.wild.destroy.17 + - towny.wild.destroy.18 + - towny.wild.destroy.21 + - towny.wild.destroy.31 + - towny.wild.destroy.37 + - towny.wild.destroy.38 + - towny.wild.destroy.39 + - towny.wild.destroy.40 + - towny.wild.destroy.50 + - towny.wild.destroy.56 + - towny.wild.destroy.73 + - towny.wild.destroy.74 + - towny.wild.destroy.78 + - towny.wild.destroy.81 + - towny.wild.destroy.82 + - towny.wild.destroy.83 + - towny.wild.destroy.86 + - towny.wild.destroy.103 + - towny.wild.destroy.106 + - towny.wild.destroy.111 + - towny.wild.destroy.115 + + g:towny_moderator: + permissions: + - towny.chat.mod + - towny.wild.switch.64 + - towny.wild.build.83 + - towny.wild.build.86 + - towny.wild.build.103 + - towny.wild.build.111 + - towny.wild.build.115 + + g:towny_admin: + permissions: + - towny.admin + - -towny.wild.destroy.119 + - -towny.wild.destroy.120 + - towny.chat.admin + +# Permission nodes for VanishNoPacket by mbaxter +# http://dev.bukkit.org/server-mods/vanish/ + + g:vanish_moderator: + permissions: + - -vanish.* + - vanish.vanish + - vanish.smokin + - vanish.nofollow + - vanish.nopickup + - vanish.preventincomingdamage + - vanish.hooks.dynmap.alwayshidden + - vanish.hooks.essentials.hide + + g:vanish_admin: + permissions: + - vanish.silentjoin + - vanish.silentquit + - vanish.silentchests \ No newline at end of file diff --git a/src/main/resources/groups.yml b/src/main/resources/groups.yml new file mode 100644 index 0000000..9c63ffd --- /dev/null +++ b/src/main/resources/groups.yml @@ -0,0 +1,74 @@ +# Group inheritance +# +# Any inherited groups prefixed with a g: are global groups +# and are inherited from the GlobalGroups.yml. +# +# Groups without the g: prefix are groups local to this world +# and are defined in the this groups.yml file. +# +# Local group inheritances define your promotion tree when using 'manpromote/mandemote' + +groups: + Default: + default: true + permissions: + - -bukkit.command.kill + inheritance: + - g:groupmanager_default + - g:bukkit_default + - g:essentials_default + - g:towny_default + info: + prefix: '&e' + build: false + suffix: '' + Builder: + default: false + permissions: [] + inheritance: + - default + - g:essentials_builder + - g:towny_builder + info: + prefix: '&2' + build: true + suffix: '' + Moderator: + default: false + permissions: [] + inheritance: + - builder + - g:groupmanager_moderator + - g:bukkit_moderator + - g:essentials_moderator + - g:towny_moderator + - g:vanish_moderator + info: + prefix: '&5' + build: true + suffix: '' + Admin: + default: false + permissions: [] + inheritance: + - moderator + - g:groupmanager_admin + - g:bukkit_admin + - g:essentials_admin + - g:towny_admin + - g:vanish_admin + info: + prefix: '&c' + build: true + suffix: '' + Owner: + default: false + permissions: + - '*' + - -vanish.* + inheritance: + - admin + info: + prefix: '&4' + build: true + suffix: '' diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..a5c2dd5 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,184 @@ +name: GroupManager +version: ${project.version} (YUMC) +main: org.anjocaido.groupmanager.GroupManager +website: http://ess.khhq.net/wiki/Group_Manager +description: Provides on-the-fly system for permissions system created by Nijikokun. But all in memory, and with flat-file saving schedule. +authors: + - AnjoCaido + - Gabriel Couto + - ElgarL +commands: + manuadd: + description: Moves a player to desired group (Adds to the file if does not exist). + usage: / | optional [world] + permissions: groupmanager.manuadd + manudel: + description: Removes any user specific configuration and make them default group. + usage: / + permissions: groupmanager.manudel + manuaddsub: + description: Adds a group to a user's subgroup list. + usage: / + permissions: groupmanager.manuaddsub + manudelsub: + description: Removes a group from a user's subgroup list. + usage: / + permissions: groupmanager.manudelsub + mangadd: + description: Adds a group to the system. + usage: / + permissions: groupmanager.mangadd + mangdel: + description: Removes a group from the system (all its users become default). + usage: / + permissions: groupmanager.mangdel + manuaddp: + description: Adds permissions directly to the user. + usage: / [permission2] [permission3].. + permissions: groupmanager.manuaddp + manudelp: + description: Removes permissions directly from the user. + usage: / [permission2] [permission3].. + permissions: groupmanager.manudelp + manuclearp: + description: Removes all permissions from a user. + usage: / + permissions: groupmanager.manuclearp + manulistp: + description: Lists all permissions of a user. + usage: / + permissions: groupmanager.manulistp + manucheckp: + description: Verifies if user has a permission, and where it comes from. + usage: / + permissions: groupmanager.manucheckp + mangaddp: + description: Adds permissions to a group. + usage: / [permission2] [permission3].. + permissions: groupmanager.mangaddp + mangdelp: + description: Removes permissions from a group. + usage: / [permission2] [permission3].. + permissions: groupmanager.mangdelp + mangclearp: + description: Removes all permissions from a group. + usage: / + permissions: groupmanager.mangclearp + manglistp: + description: Lists all permissions of a group. + usage: / + permissions: groupmanager.manglistp + mangcheckp: + description: Checks if group has a permission and where it comes from. + usage: / + permissions: groupmanager.mangcheckp + mangaddi: + description: Adds a group to another group inheritance list. + usage: / + permissions: groupmanager.mangaddi + mangdeli: + description: Removes a group from another group inheritance list. + usage: / + permissions: groupmanager.mangdeli + manuaddv: + description: Adds or replaces a variable of a user (like prefix or suffix). + usage: / + permissions: groupmanager.manuaddv + manudelv: + description: Removes a variable from a user. + usage: / + permissions: groupmanager.manudelv + manulistv: + description: Lists variables of a user (like prefix or suffix). + usage: / + permissions: groupmanager.manulistv + manucheckv: + description: Verifies a value of a variable of a user, and where it comes from. + usage: / + permissions: groupmanager.manucheckv + mangaddv: + description: Adds or replaces a variable of a group (like prefix or suffix). + usage: / + permissions: groupmanager.mangaddv + mangdelv: + description: Removes a variable from a group. + usage: / + permissions: groupmanager.mangdelv + manglistv: + description: Lists variables of a group (like prefix or suffix). + usage: / + permissions: groupmanager.manglistv + mangcheckv: + description: Verifies a value of a variable of a group, and where it comes from. + usage: / + permissions: groupmanager.mangckeckv + manwhois: + description: Lists groups and other data of a user. + usage: / + permissions: groupmanager.manwhois + tempadd: + description: Creates a temporary permissions copy for a user. + usage: / + permissions: groupmanager.tempadd + tempdel: + description: Removes the temporary permissions copy for a user. + usage: / + permissions: groupmanager.tempdel + templist: + description: Lists users in overload-permissions mode made by command /tempadd. + usage: / + permissions: groupmanager.templist + tempdelall: + description: Removes all overrides made by command /tempadd. + usage: / + permissions: groupmanager.tempdelall + mansave: + description: Saves all permissions from server to file. + usage: / + permissions: groupmanager.mansave + manload: + description: Reloads current world and config.yml, or loads given world from file to server. + usage: / [world] + permissions: groupmanager.manload + listgroups: + aliases: [manlistg] + description: Lists the groups available. + usage: / + permissions: groupmanager.listgroups + manpromote: + description: Promotes a user in the same heritage line to a higher rank. + usage: / + permissions: groupmanager.manpromote + mandemote: + description: Demotes a user in the same heritage line to a lower rank. + usage: / + permissions: groupmanager.mandemote + mantogglevalidate: + description: Toggles on/off the validating if user is online. + usage: / + permissions: groupmanager.mantogglevalidate + mantogglesave: + description: Toggles on/off the autosave. + usage: / + permissions: groupmanager.mantogglesave + manworld: + description: Prints the selected world name. + usage: / + permissions: groupmanager.manworld + manselect: + description: Selects a world to work with commands. + usage: / + permissions: groupmanager.manselect + manclear: + description: Clears world selection. Commands will work on your current world. + usage: / + permissions: groupmanager.manclear + mancheckw: + description: Obtains the path to each file a world is storing its data in (users/groups). + usage: / + permissions: groupmanager.mancheckw + +Permissions: + groupmanager.op: + description: User is treated as an op when using the GroupManager commands. + default: false diff --git a/src/main/resources/users.yml b/src/main/resources/users.yml new file mode 100644 index 0000000..eaea623 --- /dev/null +++ b/src/main/resources/users.yml @@ -0,0 +1,18 @@ +# "For a more advanced configuration example utilizing the advanced features of GroupManager, see http://pastebin.com/a8ZA0j5G" +users: + snowleo: + group: Builder + subgroups: [] + permissions: + - groupmanager.noofflineperms + KHobbits: + group: Moderator + subgroups: [] + permissions: + - groupmanager.noofflineperms + ElgarL: + group: Moderator + subgroups: [] + permissions: + - groupmanager.noofflineperms +