Java tutorial
/* * Copyright (c) 2010 SimpleServer authors (see CONTRIBUTORS) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package simpleserver.config; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.XMLConfiguration; import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXParseException; import simpleserver.Coordinate; import simpleserver.Group; import simpleserver.Player; import simpleserver.Server; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; class DTDEntityResolver implements EntityResolver { PermissionConfig c; public DTDEntityResolver(PermissionConfig c) { this.c = c; } public InputSource resolveEntity(String publicId, String systemId) { return new InputSource(c.getDTD()); } } class DTDErrorHandler implements ErrorHandler { private final String prefix = "[WARNING][permissions.xml] "; private PermissionConfig parent = null; public DTDErrorHandler(PermissionConfig p) { parent = p; } private void printExceptionInfo(SAXParseException e) { System.out.println(prefix + "L. " + e.getLineNumber() + ": " + e.getMessage()); parent.loadsuccess = false; } public void warning(SAXParseException e) { printExceptionInfo(e); } public void error(SAXParseException e) { printExceptionInfo(e); } public void fatalError(SAXParseException e) { printExceptionInfo(e); } } @SuppressWarnings("unchecked") public class PermissionConfig extends AbstractConfig { private Server server = null; private boolean isDefault = false; // true if this instance is loaded from // defaults private PermissionConfig permDefaults = null; // contains default // configuration as fallback public boolean loadsuccess = true; private XMLConfiguration config; public PermissionConfig(Server server) { super("permissions.xml"); this.server = server; isDefault = false; permDefaults = new PermissionConfig(server, true); // load defaults in the // background } public PermissionConfig(Server server, boolean isDefault) { super("permissions.xml"); this.server = server; this.isDefault = isDefault; loadDefaults(); } public InputStream getDTD() { return super.getClass().getResourceAsStream(resourceLocation + "/permissions.dtd"); } // prepare a new XMLConfiguration Object for loading private XMLConfiguration initConf(boolean validate) { XMLConfiguration conf = new XMLConfiguration(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = null; try { dbf.setValidating(validate); db = dbf.newDocumentBuilder(); } catch (ParserConfigurationException e) { } if (db != null) { db.setEntityResolver(new DTDEntityResolver(this)); db.setErrorHandler(new DTDErrorHandler(this)); conf.setDocumentBuilder(db); } conf.setExpressionEngine(new XPathExpressionEngine()); conf.setDelimiterParsingDisabled(true); return conf; } // replacement for GroupList.groupExists public boolean groupExists(int id) { List ids = config.getList("/groups/group/@id"); return ids.contains(String.valueOf(id)); } // replacement for GroupList.getGroup public Group getGroup(int id) { if (!groupExists(id)) { return null; } String pathpart = "/groups/group[@id='" + id + "']/@"; String name = config.getString(pathpart + "name", ""); String showtitle = config.getString(pathpart + "showTitle", ""); String isadmin = config.getString(pathpart + "ignoreChestlocks", ""); String color = config.getString(pathpart + "color", ""); /* set defaults for missing attributes */ if (name.equals("")) { name = "NamelessGroup"; } if (color.equals("")) { color = "f"; } if (showtitle.equals("")) { showtitle = "false"; } if (isadmin.equals("")) { isadmin = "false"; } return new Group(name, Boolean.valueOf(showtitle), Boolean.valueOf(isadmin), color); } private int getIPGroup(String ipAddress) { String group = ""; group = config.getString("/members/ip[@address='" + ipAddress + "']/@group", ""); if (group.equals("")) { return server.options.getInt("defaultGroup"); } return Integer.valueOf(group); } private String xpath_lcase(String attr) { return "translate(" + attr + ",'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')"; } public int getNameGroup(String name) { name = name.toLowerCase(); String group = ""; group = config.getString("/members/player[" + xpath_lcase("@name") + "='" + name + "']/@group", ""); if (group.equals("")) { return server.options.getInt("defaultGroup"); } return Integer.valueOf(group); } // replacement for (IP)MemberList.getGroup and does the former work of // Player.updateGroup public int getPlayerGroup(Player player) { return getPlayerGroup(player.getName(), player.getIPAddress()); } private int getPlayerGroup(String name, String ip) { int grp = 0; int nameGroup = getNameGroup(name); int ipGroup = getIPGroup(ip); if (ipGroup > nameGroup) { grp = ipGroup; } else { grp = nameGroup; } return grp; } // replacement for MemberList.setGroup public void setPlayerGroup(Player player, int group) { setPlayerGroup(player.getName(), group); } public void setPlayerGroup(String name, int group) { name = name.toLowerCase(); String val = config.getString("/members/player[" + xpath_lcase("@name") + "='" + name + "']/@group", ""); if (val.equals("")) { config.addProperty("/members player@name", name); config.addProperty("/members/player[@name='" + name + "'][1] @group", ""); } config.setProperty("/members/player[@name='" + name + "']/@group", String.valueOf(group)); server.updateGroup(name); save(); } public void setIPGroup(String ip, int group) { String val = config.getString("/members/ip[" + xpath_lcase("@address") + "='" + ip + "']/@group", ""); if (val.equals("")) { config.addProperty("/members ip@address", ip); config.addProperty("/members/ip[@address='" + ip + "'][1] @group", ""); } config.setProperty("/members/ip[@address='" + ip + "']/@group", String.valueOf(group)); server.updateGroups(); save(); } // replaces Group.isMember with nickname permissions public boolean includesPlayer(String allowstr, Player player) { if (allowstr == null || allowstr.equals("")) { return false; } String[] str = allowstr.split(";"); boolean isgrpmember = false; if (!str[0].equals("") && !str[0].equals("-")) { isgrpmember = parseGroups(str[0]).contains(player.getGroupId()); } if (str.length < 2) { return isgrpmember; } String[] nicks = str[1].split(","); for (int i = 0; i < nicks.length; i++) { nicks[i] = nicks[i].toLowerCase(); } return isgrpmember || Arrays.asList(nicks).contains(player.getName().toLowerCase()); } // moved from class Group, parses ranges and listings of groups private ImmutableSet<Integer> parseGroups(String idString) { String[] segments = idString.split(","); ImmutableSortedSet.Builder<Integer> groups = ImmutableSortedSet.naturalOrder(); for (String segment : segments) { if (segment.matches("^\\s*$")) { continue; } try { groups.add(Integer.valueOf(segment)); continue; } catch (NumberFormatException e) { } if (segment.indexOf('+') == segment.length() - 1) { int num = 0; try { num = Integer.valueOf(segment.split("\\+")[0]); } catch (NumberFormatException e) { System.out.println("Unable to parse group: " + segment); continue; } List ids = config.getList("/groups/group/@id"); for (int j = 0; j < ids.size(); j++) { int n = Integer.valueOf(ids.get(j).toString()); if (n >= num) { groups.add(n); } } continue; } String[] range = segment.split("-"); if (range.length != 2) { System.out.println("Unable to parse group: " + segment); continue; } int low; int high; try { low = Integer.valueOf(range[0]); high = Integer.valueOf(range[1]); } catch (NumberFormatException e) { System.out.println("Unable to parse group: " + segment); continue; } if (low > high) { System.out.println("Unable to parse group: " + segment); continue; } for (int k = low; k <= high; ++k) { groups.add(k); } } return groups.build(); } // replacement for BlockList.playerAllowed // return: [place, destroy, use, take] public boolean[] getPlayerBlockPermissions(Player player, Coordinate blockCoord, int bID) { boolean[] perms = new boolean[4]; if (blockCoord == null) { blockCoord = coordinateFromPlayer(player); } ArrayList<String> attrs = new ArrayList<String>(); attrs.add("allowPlace"); attrs.add("allowDestroy"); attrs.add("allowUse"); attrs.add("allowTake"); String pathpart = "/permissions/blocks/block[@id='" + String.valueOf(bID) + "']/@"; // get global permissions for (int i = 0; i < attrs.size(); i++) { String permstr = config.getString("/permissions/blocks/@" + attrs.get(i), ""); if (permstr.equals("")) { perms[i] = true; } else { perms[i] = includesPlayer(permstr, player); } } // single block tag overrides the areawide allowPlace String place_allow = config.getString(pathpart + "allow", ""); if (!place_allow.equals("")) { perms[0] = includesPlayer(place_allow, player); } String place_disallow = config.getString(pathpart + "disallow", ""); if (!place_disallow.equals("")) { perms[0] = !includesPlayer(place_disallow, player); } String xpath = getAreanodeForCoordinate(blockCoord); String[] areas = getAllAreasFromAreaXPath(xpath); for (String area : areas) { String areapath = "//area[@name='" + area + "']"; // get area wide permissions for (int n = 0; n < attrs.size(); n++) { String permstr = config.getString(areapath + "/permissions/blocks/@" + attrs.get(n), ""); if (!permstr.equals("")) { perms[n] = includesPlayer(permstr, player); } } // single block tag overrides the areawide allowPlace place_allow = config.getString(areapath + pathpart + "allow", ""); if (!place_allow.equals("")) { perms[0] = includesPlayer(place_allow, player); } place_disallow = config.getString(areapath + pathpart + "disallow", ""); if (!place_disallow.equals("")) { perms[0] = !includesPlayer(place_disallow, player); } } return perms; } // replacement for CommandList.playerAllowed public boolean playerCommandAllowed(String cmd, Player player) { boolean allowed = false; String pathpart = "/permissions/commands/command[@name='" + cmd + "']/@"; String globalpermission = config.getString(pathpart + "allow", ""); if (!globalpermission.equals("")) { allowed = includesPlayer(globalpermission, player); } // get all parent areas and check them incrementally String xpath = getAreanodeForCoordinate(coordinateFromPlayer(player)); String[] areas = getAllAreasFromAreaXPath(xpath); for (String area : areas) { String path = "//area[@name='" + area + "']"; String areaallow = config.getString(path + pathpart + "allow", ""); String areadisallow = config.getString(path + pathpart + "disallow", ""); if (!areaallow.equals("")) { allowed = includesPlayer(areaallow, player); } if (!areadisallow.equals("")) { allowed = !includesPlayer(areadisallow, player); } } return allowed; } // replacement for CommandList.getAliases public String[] getCommandAliases(String name) { String aliasstr = config.getString("/permissions/commands/command[@name='" + name + "']/@aliases", ""); if (aliasstr.equals("")) { return new String[] {}; } return aliasstr.split(","); } // replacement for CommandList.lookupCommand public String lookupCommand(String name) { List cmds = config.getList("/permissions/commands/command/@name"); if (cmds.contains(name)) { return name; } for (int i = 0; i < cmds.size(); i++) { // alias? String cmd = (String) cmds.get(i); String[] aliases = getCommandAliases(cmd); if (Arrays.asList(aliases).contains(name)) { return cmd; } } // not found :( return null; } // add new commands from the defaults to the config private void checkNewCommands() { List cmds = config.getList("/permissions/commands/command/@name"); List defcmds = permDefaults.config.getList("/permissions/commands/command/@name"); for (Object cmdname : defcmds) { cmdname = cmdname.toString(); if (!cmds.contains(cmdname)) { config.addProperty("/permissions/commands command@name", cmdname); String path = "/permissions/commands/command[@name='" + cmdname + "']"; String[] attrs = { "@aliases", "@allow", "@hidden", "@forward" }; for (String attr : attrs) { String val = permDefaults.config.getString(path + "/" + attr, ""); if (!val.equals("")) { config.addProperty(path + "[1] " + attr, val); } } } } } // replacement for CommandList.setGroup public void setCommandGroup(String cmd, int grp) { String val = config.getString("/permissions/commands/command[@name='" + cmd + "']/@allow", ""); if (val.equals("")) { config.addProperty("/permissions/commands/ command@name", cmd); config.addProperty("/permissions/commands/command[@name='" + cmd + "'][1] @allow", String.valueOf(grp)); } config.setProperty("/permissions/commands/command[@name='" + cmd + "']/@allow", String.valueOf(grp)); } public static String joinStrings(String[] strs, String delim) { String ret = ""; if (strs == null || strs.length == 0) { return ""; } if (strs.length == 1) { return strs[0]; } for (int i = 0; i < (strs.length - 1); i++) { ret += strs[i] + delim; } ret += strs[strs.length - 1]; return ret; } public String getCurrentArea(Player player) { return getAreanameForCoordinate(coordinateFromPlayer(player)); } public Coordinate coordinateFromPlayer(Player player) { int x = (int) Math.round(player.getX()); byte y = (byte) Math.round(player.getY()); int z = (int) Math.round(player.getZ()); return new Coordinate(x, y, z); } public String getAreanameForCoordinate(Coordinate coord) { String node = getAreanodeForCoordinate(coord); if (node.equals("")) { return ""; } return config.getString(node + "/@name"); } // Returns full XPath to the area node -> contains also names of parent areas private String getAreanodeForCoordinate(Coordinate coord) { String nodepath = ""; boolean found = false; while (!found) { found = true; List areas = config.getList(nodepath + "/areas/area/@name"); List starts = config.getList(nodepath + "/areas/area/@start"); List ends = config.getList(nodepath + "/areas/area/@end"); if (areas.size() == 0 || areas.size() != starts.size() || areas.size() != ends.size()) { break; } for (int i = 0; i < areas.size(); i++) { if (areaContainsCoordinate(parseCoords(starts.get(i).toString()), parseCoords(ends.get(i).toString()), coord)) { nodepath += "/areas/area[@name='" + areas.get(i) + "']"; found = false; break; } } } return nodepath; } // input like: /areas/area[@name="outer"]/areas/area[@name="Inner"] // output like: ["outer","inner"] private String[] getAllAreasFromAreaXPath(String path) { if (path.equals("")) { return new String[] {}; } ArrayList<String> areas = new ArrayList<String>(); for (int i = 0; i < path.length(); i++) { int start = path.indexOf("'", i); if (start == -1) { break; } int end = path.indexOf("'", start + 1); areas.add(path.substring(start + 1, end)); i = end; } return areas.toArray(new String[areas.size()]); } private boolean areaContainsCoordinate(Coordinate start, Coordinate end, Coordinate coord) { if (start.dimension() != coord.dimension() || end.dimension() != coord.dimension()) { return false; } Coordinate max = new Coordinate(Math.max(start.x(), end.x()), (byte) Math.max(start.y(), end.y()), Math.max(start.z(), end.z())); Coordinate min = new Coordinate(Math.min(start.x(), end.x()), (byte) Math.min(start.y(), end.y()), Math.min(start.z(), end.z())); if (max.y() == 0) { max = max.setY((byte) 128); } return coord.x() >= min.x() && coord.z() >= min.z() && coord.y() >= min.y() && coord.x() <= max.x() && coord.z() <= max.z() && coord.y() <= max.y(); } private Coordinate parseCoords(String c) { if (c == null || c.length() == 0) { return null; } String[] coords = c.split(","); if (coords.length < 2) { return null; } Integer x = Integer.valueOf(coords[0]); Integer z = Integer.valueOf(coords[1]); byte y = 0; if (coords.length == 3) { y = (byte) Integer.valueOf(coords[2]).intValue(); } return new Coordinate(x, y, z); } public void createPlayerArea(Player player) { String name = player.getName().toLowerCase(); if (playerHasArea(player)) { return; } config.addProperty("/areas area@owner", name); String path = "/areas/area[@owner='" + name + "']"; config.addProperty(path + "[1] @name", name + "s private area"); config.addProperty(path + "[1] @start", player.areastart.x() + "," + player.areastart.z()); config.addProperty(path + "[1] @end", player.areaend.x() + "," + player.areaend.z()); config.addProperty(path + "[1] permissions/blocks@allowPlace", ";" + name); config.addProperty(path + "[1]/permissions/blocks @allowDestroy", ";" + name); config.addProperty(path + "[1]/permissions/blocks @allowUse", ";" + name); config.addProperty(path + "[1]/permissions/blocks @allowTake", ";" + name); } public void removePlayerArea(Player player) { String name = player.getName().toLowerCase(); if (!playerHasArea(player)) { return; } String path = "/areas/area[@owner='" + name + "']"; config.clearTree(path); if (config.getList("/areas/area/@name").size() == 0) { config.addProperty("/ areas", " "); // add areas tag back } } public void renamePlayerArea(Player player, String label) { String name = player.getName().toLowerCase(); if (!playerHasArea(player)) { return; } String path = "/areas/area[@owner='" + name + "']"; config.setProperty(path + "/ @name", label); } public boolean hasAreaWithName(String label) { return !config.getString("/areas/area[@name='" + label + "']/@name", "").equals(""); } public boolean playerHasArea(Player player) { String name = player.getName().toLowerCase(); return !config.getString("/areas/area[@owner='" + name + "']/@owner", "").equals(""); } public boolean commandShouldPassThroughToMod(String cmd) { String val = config.getString("/permissions/commands/command[@name='" + cmd + "']/@forward", ""); if (val.equals("true")) { return true; } return false; } public boolean commandIsHidden(String cmd) { String val = config.getString("/permissions/commands/command[@name='" + cmd + "']/@hidden", ""); if (val.equals("true")) { return true; } return false; } @Override protected void loadHeader() { // No header for XML config required } @Override public void load() { if (isDefault) { return; // should not be called in default config! } XMLConfiguration confbuff = null; try { // load stored config loadsuccess = true; InputStream stream = new FileInputStream(getFile()); confbuff = initConf(true); confbuff.load(stream); if (!loadsuccess) { loadsuccess = true; System.out.println("Trying to load permissions.xml ignoring DTD warnings..."); stream = new FileInputStream(getFile()); confbuff = initConf(false); confbuff.load(stream); } config = confbuff; // No problems loading -> set loaded config as real // config checkNewCommands(); // append new commands found in default to current // config } catch (FileNotFoundException e) { System.out.println("Trying to convert old configuration files..."); if (new ConfToXml().convertFiles()) { server.kits.load(); load(); } else { System.out.println(getFilename() + " is missing. Loading defaults."); loadDefaults(); save(); } } catch (ConfigurationException e) { System.out.println("[SimpleServer] " + e); System.out.println("[SimpleServer] Failed to load " + getFilename()); if (config != null) { System.out.println("[SimpleServer] Warning: permission.xml NOT reloaded!"); System.out.println(" Saving now will overwrite your changes!"); } loadsuccess = false; } } public void loadDefaults() { try { config = initConf(true); InputStream stream = getResourceStream(); config.load(stream); loadsuccess = true; } catch (ConfigurationException ex) { System.out.println("[SimpleServer] " + ex); System.out.println("[SimpleServer] Failed to load defaults for " + getFilename()); loadsuccess = false; } } @Override public void save() { try { OutputStream stream = new FileOutputStream(getFile()); config.save(stream); } catch (Exception e) { System.out.println("[SimpleServer] " + e); System.out.println("[SimpleServer] Failed to save " + getFilename()); } } }