/*
* PermissionsEx - Permissions plugin for Bukkit
* Copyright (C) 2011 t3hk0d3 http://www.tehkode.ru
*
* 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 pex.permissions.backends;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.WorldServer;
import pex.permissions.PermissionBackend;
import pex.permissions.PermissionGroup;
import pex.permissions.PermissionManager;
import pex.permissions.PermissionUser;
import pex.permissions.backends.sql.SQLConnection;
import pex.permissions.backends.sql.SQLEntity;
import pex.permissions.backends.sql.SQLGroup;
import pex.permissions.backends.sql.SQLUser;
import pex.utils.StringUtils;
/**
*
* @author code
*/
public class SQLBackend extends PermissionBackend {
protected Map<String, String[]> worldInheritanceCache = new HashMap<String, String[]>();
public SQLConnection sql;
public SQLBackend(PermissionManager manager, net.minecraftforge.common.Configuration config) {
super(manager, config);
}
@Override
public void initialize() {
String dbDriver = config.get("permissions", "backends_sql_driver", "mysql").getString();
String dbUri = config.get("permissions", "backends_sql_uri", "mysql://localhost/exampledb").getString();
String dbUser = config.get("permissions", "backends_sql_user", "databaseuser").getString();
String dbPassword = config.get("permissions", "backends_sql_password", "databasepassword").getString();
sql = new SQLConnection(dbUri, dbUser, dbPassword, dbDriver);
Logger.getLogger("Minecraft").info("[PermissionsEx-SQL] Successfuly connected to database");
setupAliases(config);
deployTables(dbDriver);
}
@Override
public PermissionUser getUser(String name) {
return new SQLUser(name, manager, sql);
}
@Override
public PermissionGroup getGroup(String name) {
return new SQLGroup(name, manager, sql);
}
@Override
public PermissionGroup getDefaultGroup(String worldName) {
try {
ResultSet result;
if (worldName == null) {
result = sql.select("SELECT `name` FROM `permissions_entity` WHERE `type` = ? AND `default` = 1 LIMIT 1", SQLEntity.Type.GROUP.ordinal());
if (!result.next()) {
throw new RuntimeException("There is no default group set, this is a serious issue");
}
} else {
result = sql.select("SELECT `name` FROM `permissions` WHERE `permission` = 'default' AND `value` = 'true' AND `type` = ? AND `world` = ?", SQLEntity.Type.GROUP.ordinal(), worldName);
if (!result.next()) {
return null;
}
}
return manager.getGroup(result.getString("name"));
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void setDefaultGroup(PermissionGroup group, String worldName) {
try {
if (worldName == null) {
// Reset default flag
sql.executeUpdate("UPDATE `permissions_entity` SET `default` = 0 WHERE `type` = ? AND `default` = 1 LIMIT 1", SQLEntity.Type.GROUP.ordinal());
// Set default flag
sql.executeUpdate("UPDATE `permissions_entity` SET `default` = 1 WHERE `type` = ? AND `name` = ? LIMIT 1", SQLEntity.Type.GROUP.ordinal(), group.getName());
} else {
sql.executeUpdate("DELETE FROM `permissions` WHERE `permission` = 'default' AND `world` = ? AND `type` = ?", worldName, SQLEntity.Type.GROUP.ordinal());
sql.executeUpdate("INSERT INTO `permissions` (`name`, `permission`, `type`, `world`, `value`) VALUES (?, 'default', ?, ?, 'true')", group.getName(), SQLEntity.Type.GROUP.ordinal(), worldName);
}
} catch (SQLException e) {
throw new RuntimeException("Failed to set default group", e);
}
}
@Override
public PermissionGroup[] getGroups() {
String[] groupNames = SQLEntity.getEntitiesNames(sql, SQLEntity.Type.GROUP, false);
List<PermissionGroup> groups = new LinkedList<PermissionGroup>();
for (String groupName : groupNames) {
groups.add(manager.getGroup(groupName));
}
Collections.sort(groups);
return groups.toArray(new PermissionGroup[0]);
}
@Override
public PermissionUser[] getRegisteredUsers() {
String[] userNames = SQLEntity.getEntitiesNames(sql, SQLEntity.Type.USER, false);
PermissionUser[] users = new PermissionUser[userNames.length];
int index = 0;
for (String groupName : userNames) {
users[index++] = manager.getUser(groupName);
}
return users;
}
protected final void setupAliases(net.minecraftforge.common.Configuration config) {
/*
* ConfigurationSection aliases =
* config.getConfigurationSection("permissions.backends.sql.aliases");
*
* if (aliases == null) { return; }
*
* for (Map.Entry<String, Object> entry :
* aliases.getValues(false).entrySet()) { sql.setAlias(entry.getKey(),
* entry.getValue().toString()); }
*/
}
protected final void deployTables(String driver) {
if (sql.isTableExist("permissions")) {
return;
}
try {
InputStream databaseDumpStream = getClass().getResourceAsStream("/sql/" + driver + ".sql");
if (databaseDumpStream == null) {
throw new Exception("Can't find appropriate database dump for used database (" + driver + "). Is it bundled?");
}
String deploySQL = StringUtils.readStream(databaseDumpStream);
Logger.getLogger("Minecraft").info("Deploying default database scheme");
for (String sqlQuery : deploySQL.trim().split(";")) {
sqlQuery = sqlQuery.trim();
if (sqlQuery.isEmpty()) {
continue;
}
sqlQuery = sqlQuery + ";";
sql.executeUpdate(sqlQuery);
}
Logger.getLogger("Minecraft").info("Database scheme deploying complete.");
} catch (Exception e) {
Logger.getLogger("Minecraft").severe("SQL Error: " + e.getMessage());
Logger.getLogger("Minecraft").severe("Deploying of default scheme failed. Please initialize database manually using " + driver + ".sql");
}
}
@Override
public void dumpData(OutputStreamWriter writer) throws IOException {
// Users
for (PermissionUser user : manager.getUsers()) {
// Basic info (Prefix/Suffix)
String prefix = user.getOwnPrefix();
String suffix = user.getOwnSuffix();
writer.append("INSERT INTO `permissions_entity` ( `name`, `type`, `prefix`, `suffix` ) VALUES ( '" + user.getName() + "', 1, '" + (prefix == null ? "" : prefix) + "','" + (suffix == null ? "" : suffix) + "' );\n");
// Inheritance
for (String group : user.getGroupsNames()) {
writer.append("INSERT INTO `permissions_inheritance` ( `child`, `parent`, `type` ) VALUES ( '" + user.getName() + "', '" + group + "', 1);\n");
}
// Permissions
for (Map.Entry<String, String[]> entry : user.getAllPermissions().entrySet()) {
for (String permission : entry.getValue()) {
String world = entry.getKey();
if (world == null) {
world = "";
}
writer.append("INSERT INTO `permissions` ( `name`, `type`, `permission`, `world`, `value` ) VALUES ('" + user.getName() + "', 1, '" + permission + "', '" + world + "', ''); \n");
}
}
// Options
for (Map.Entry<String, Map<String, String>> entry : user.getAllOptions().entrySet()) {
for (Map.Entry<String, String> option : entry.getValue().entrySet()) {
String value = option.getValue().replace("'", "\\'");
String world = entry.getKey();
if (world == null) {
world = "";
}
writer.append("INSERT INTO `permissions` ( `name`, `type`, `permission`, `world`, `value` ) VALUES ('" + user.getName() + "', 1, '" + option.getKey() + "', '" + world + "', '" + value + "' );\n");
}
}
}
PermissionGroup defaultGroup = manager.getDefaultGroup();
// Groups
for (PermissionGroup group : manager.getGroups()) {
// Basic info (Prefix/Suffix)
writer.append("INSERT INTO `permissions_entity` ( `name`, `type`, `prefix`, `suffix`, `default` ) VALUES ( '" + group.getName() + "', 0, '" + group.getOwnPrefix() + "','" + group.getOwnSuffix() + "', " + (group.equals(defaultGroup) ? "1" : "0") + " );\n");
// Inheritance
for (String parent : group.getParentGroupsNames()) {
writer.append("INSERT INTO `permissions_inheritance` ( `child`, `parent`, `type` ) VALUES ( '" + group.getName() + "', '" + parent + "', 0);\n");
}
// Permissions
for (Map.Entry<String, String[]> entry : group.getAllPermissions().entrySet()) {
for (String permission : entry.getValue()) {
String world = entry.getKey();
if (world == null) {
world = "";
}
writer.append("INSERT INTO `permissions` ( `name`, `type`, `permission`, `world`, `value`) VALUES ('" + group.getName() + "', 0, '" + permission + "', '" + world + "', '');\n");
}
}
// Options
for (Map.Entry<String, Map<String, String>> entry : group.getAllOptions().entrySet()) {
for (Map.Entry<String, String> option : entry.getValue().entrySet()) {
String value = option.getValue().replace("'", "\\'");
String world = entry.getKey();
if (world == null) {
world = "";
}
writer.append("INSERT INTO `permissions` ( `name`, `type`, `permission`, `world`, `value` ) VALUES ('" + group.getName() + "', 0, '" + option.getKey() + "', '" + world + "', '" + value + "' );\n");
}
}
}
// World-inheritance
for (WorldServer world : MinecraftServer.getServer().worldServers) {
String[] parentWorlds = manager.getWorldInheritance(String.valueOf(world.provider.dimensionId));
if (parentWorlds.length == 0) {
continue;
}
for (String parentWorld : parentWorlds) {
writer.append("INSERT INTO `permissions_inheritance` ( `child`, `parent`, `type` ) VALUES ( '" + String.valueOf(world.provider.dimensionId) + "', '" + parentWorld + "', 2);\n");
}
}
writer.flush();
}
@Override
public String[] getWorldInheritance(String world) {
if (world == null || world.isEmpty()) {
return new String[0];
}
if (!worldInheritanceCache.containsKey(world)) {
try {
ResultSet result = sql.select("SELECT `parent` FROM `permissions_inheritance` WHERE `child` = ? AND `type` = 2;", world);
LinkedList<String> worldParents = new LinkedList<String>();
while (result.next()) {
worldParents.add(result.getString("parent"));
}
worldInheritanceCache.put(world, worldParents.toArray(new String[0]));
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
return worldInheritanceCache.get(world);
}
@Override
public void setWorldInheritance(String worldName, String[] parentWorlds) {
if (worldName == null || worldName.isEmpty()) {
return;
}
try {
sql.executeUpdate("DELETE FROM `permissions_inheritance` WHERE `child` = ? AND `type` = 2", worldName);
List<Object[]> records = new LinkedList<Object[]>();
for (String parentWorld : parentWorlds) {
records.add(new Object[] { worldName, parentWorld, 2 });
}
sql.insert("permissions_inheritance", new String[] { "child", "parent", "type" }, records);
worldInheritanceCache.put(worldName, parentWorlds);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void reload() {
worldInheritanceCache.clear();
}
}