diff --git a/TShockAPI/Rest/RestManager.cs b/TShockAPI/Rest/RestManager.cs index 4fd20273..ed79492e 100644 --- a/TShockAPI/Rest/RestManager.cs +++ b/TShockAPI/Rest/RestManager.cs @@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using HttpServer; @@ -36,395 +37,393 @@ namespace TShockAPI public void RegisterRestfulCommands() { - Rest.Register(new RestCommand("/status", Status) {RequiresToken = false}); - Rest.Register(new RestCommand("/tokentest", TokenTest) {RequiresToken = true}); + // Server Commands + Rest.Register(new RestCommand("/v2/server/broadcast", ServerBroadcast)); + Rest.Register(new RestCommand("/v2/server/off", ServerOff)); + Rest.Register(new RestCommand("/v2/server/rawcmd", ServerCommand)); + Rest.Register(new RestCommand("/v2/server/status", ServerStatusV2) { RequiresToken = false }); + Rest.Register(new RestCommand("/tokentest", ServerTokenTest)); + Rest.Register(new RestCommand("/status", ServerStatus) { RequiresToken = false }); - Rest.Register(new RestCommand("/v2/users/activelist", UserListV2) { RequiresToken = true }); - Rest.Register(new RestCommand("/v2/users/read", UserInfoV2) { RequiresToken = true }); - Rest.Register(new RestCommand("/v2/users/destroy", UserDestroyV2) { RequiresToken = true }); - Rest.Register(new RestCommand("/v2/users/update", UserUpdateV2) { RequiresToken = true }); + // User Commands + Rest.Register(new RestCommand("/v2/users/activelist", UserActiveListV2)); + Rest.Register(new RestCommand("/v2/users/create", UserCreateV2)); + Rest.Register(new RestCommand("/v2/users/list", UserListV2)); + Rest.Register(new RestCommand("/v2/users/read", UserInfoV2)); + Rest.Register(new RestCommand("/v2/users/destroy", UserDestroyV2)); + Rest.Register(new RestCommand("/v2/users/update", UserUpdateV2)); - Rest.Register(new RestCommand("/bans/create", BanCreate) {RequiresToken = true}); - Rest.Register(new RestCommand("/v2/bans/read", BanInfoV2) { RequiresToken = true }); - Rest.Register(new RestCommand("/v2/bans/destroy", BanDestroyV2) { RequiresToken = true }); + // Ban Commands + Rest.Register(new RestCommand("/bans/create", BanCreate)); + Rest.Register(new RestCommand("/v2/bans/list", BanListV2)); + Rest.Register(new RestCommand("/v2/bans/read", BanInfoV2)); + Rest.Register(new RestCommand("/v2/bans/destroy", BanDestroyV2)); - Rest.Register(new RestCommand("/v2/lists/bans", BanListIPs) { RequiresToken = true }); - Rest.Register(new RestCommand("/lists/players", PlayerList) {RequiresToken = true}); - Rest.Register(new RestCommand("/v2/lists/players", PlayerListV2) { RequiresToken = true }); + // World Commands + Rest.Register(new RestCommand("/world/read", WorldRead)); + Rest.Register(new RestCommand("/world/meteor", WorldMeteor)); + Rest.Register(new RestCommand("/world/bloodmoon/{bool}", WorldBloodmoon)); + Rest.Register(new RestCommand("/v2/world/save", WorldSave)); + Rest.Register(new RestCommand("/v2/world/autosave/state/{bool}", WorldChangeSaveSettings)); + Rest.Register(new RestCommand("/v2/world/butcher", WorldButcher)); - Rest.Register(new RestCommand("/world/read", WorldRead) {RequiresToken = true}); - Rest.Register(new RestCommand("/world/meteor", WorldMeteor) {RequiresToken = true}); - Rest.Register(new RestCommand("/world/bloodmoon/{bool}", WorldBloodmoon) {RequiresToken = true}); - Rest.Register(new RestCommand("/v2/world/save", WorldSave) { RequiresToken = true}); - Rest.Register(new RestCommand("/v2/world/autosave/state/{bool}", ChangeWorldSaveSettings) { RequiresToken = true }); - Rest.Register(new RestCommand("/v2/world/butcher", Butcher) {RequiresToken = true}); + // Player Commands + Rest.Register(new RestCommand("/lists/players", PlayerList)); + Rest.Register(new RestCommand("/v2/players/list", PlayerListV2)); + Rest.Register(new RestCommand("/v2/players/read", PlayerReadV2)); + Rest.Register(new RestCommand("/v2/players/kick", PlayerKickV2)); + Rest.Register(new RestCommand("/v2/players/ban", PlayerBanV2)); + Rest.Register(new RestCommand("/v2/players/kill", PlayerKill)); + Rest.Register(new RestCommand("/v2/players/mute", PlayerMute)); + Rest.Register(new RestCommand("/v2/players/unmute", PlayerUnMute)); - Rest.Register(new RestCommand("/v2/players/read", PlayerReadV2) { RequiresToken = true }); - Rest.Register(new RestCommand("/v2/players/kick", PlayerKickV2) { RequiresToken = true }); - Rest.Register(new RestCommand("/v2/players/ban", PlayerBanV2) { RequiresToken = true }); - Rest.Register(new RestCommand("/v2/players/kill", PlayerKill) {RequiresToken = true}); - Rest.Register(new RestCommand("/v2/players/mute", PlayerMute) {RequiresToken = true}); - Rest.Register(new RestCommand("/v2/players/unmute", PlayerUnMute) {RequiresToken = true}); - - Rest.Register(new RestCommand("/v2/server/broadcast", Broadcast) { RequiresToken = true}); - Rest.Register(new RestCommand("/v2/server/off", Off) {RequiresToken = true}); - Rest.Register(new RestCommand("/v2/server/rawcmd", ServerCommand) {RequiresToken = true}); + // Group Commands + Rest.Register(new RestCommand("/v2/groups/list", GroupList)); + Rest.Register(new RestCommand("/v2/groups/read", GroupInfo)); + Rest.Register(new RestCommand("/v2/groups/destroy", GroupDestroy)); + Rest.Register(new RestCommand("/v2/groups/create", GroupCreate)); + Rest.Register(new RestCommand("/v2/groups/update", GroupUpdate)); } #region RestServerMethods private object ServerCommand(RestVerbs verbs, IParameterCollection parameters) { - if (parameters["cmd"] != null && parameters["cmd"].Trim() != "") - { - TSRestPlayer tr = new TSRestPlayer(); - RestObject ro = new RestObject("200"); - Commands.HandleCommand(tr, parameters["cmd"]); - foreach (string s in tr.GetCommandOutput()) - { - ro.Add("response", s); - } - return ro; - } - RestObject fail = new RestObject("400"); - fail["response"] = "Missing or blank cmd parameter."; - return fail; + if (string.IsNullOrWhiteSpace(parameters["cmd"])) + return RestMissingParam("cmd"); + + TSRestPlayer tr = new TSRestPlayer(); + Commands.HandleCommand(tr, parameters["cmd"]); + return RestResponse(string.Join("\n", tr.GetCommandOutput())); } - private object Off(RestVerbs verbs, IParameterCollection parameters) + private object ServerOff(RestVerbs verbs, IParameterCollection parameters) { - bool confirm; - bool.TryParse(parameters["confirm"], out confirm); - bool nosave; - bool.TryParse(parameters["nosave"], out nosave); + if (!GetBool(parameters["confirm"], false)) + return RestInvalidParam("confirm"); - if (confirm == true) + if (!GetBool(parameters["nosave"], false)) + WorldGen.saveWorld(); + Netplay.disconnect = true; + + // Inform players the server is shutting down + var msg = string.IsNullOrWhiteSpace(parameters["message"]) ? "Server is shutting down" : parameters["message"]; + foreach (TSPlayer player in TShock.Players.Where(p => null != p)) { - if (!nosave) - WorldGen.saveWorld(); - Netplay.disconnect = true; - RestObject reply = new RestObject("200"); - reply["response"] = "The server is shutting down."; - return reply; + TShock.Utils.ForceKick(player, msg); } - RestObject fail = new RestObject("400"); - fail["response"] = "Invalid/missing confirm switch, and/or missing nosave switch."; - return fail; + return RestResponse("The server is shutting down"); } - private object Broadcast(RestVerbs verbs, IParameterCollection parameters) + private object ServerBroadcast(RestVerbs verbs, IParameterCollection parameters) { - if (parameters["msg"] != null && parameters["msg"].Trim() != "") - { - TShock.Utils.Broadcast(parameters["msg"]); - RestObject reply = new RestObject("200"); - reply["response"] = "The message was broadcasted successfully."; - return reply; - } - RestObject fail = new RestObject("400"); - fail["response"] = "Broadcast failed."; - return fail; + var msg = parameters["msg"]; + if (string.IsNullOrWhiteSpace(msg)) + return RestMissingParam("msg"); + TShock.Utils.Broadcast(msg); + return RestResponse("The message was broadcasted successfully"); } - #endregion - - #region RestMethods - - private object TokenTest(RestVerbs verbs, IParameterCollection parameters) - { - return new Dictionary - {{"status", "200"}, {"response", "Token is valid and was passed through correctly."}}; - } - - private object Status(RestVerbs verbs, IParameterCollection parameters) + private object ServerStatus(RestVerbs verbs, IParameterCollection parameters) { if (TShock.Config.EnableTokenEndpointAuthentication) - return new RestObject("403") {Error = "Server settings require a token for this API call."}; + return RestError("Server settings require a token for this API call"); - var activeplayers = Main.player.Where(p => p != null && p.active).ToList(); - string currentPlayers = string.Join(", ", activeplayers.Select(p => p.name)); + var activeplayers = Main.player.Where(p => null != p && p.active).ToList(); + return new RestObject() + { + {"name", TShock.Config.ServerNickname}, + {"port", Convert.ToString(TShock.Config.ServerPort)}, + {"playercount", Convert.ToString(activeplayers.Count())}, + {"players", string.Join(", ", activeplayers.Select(p => p.name))}, + }; + } - var ret = new RestObject("200"); - ret["name"] = TShock.Config.ServerNickname; - ret["port"] = Convert.ToString(TShock.Config.ServerPort); - ret["playercount"] = Convert.ToString(activeplayers.Count()); - ret["players"] = currentPlayers; + private object ServerStatusV2(RestVerbs verbs, IParameterCollection parameters) + { + if (TShock.Config.EnableTokenEndpointAuthentication) + return RestError("Server settings require a token for this API call"); - ret["maxplayers"] = TShock.Config.MaxSlots; + var ret = new RestObject() + { + {"name", TShock.Config.ServerNickname}, + {"port", TShock.Config.ServerPort}, + {"playercount", Main.player.Where(p => null != p && p.active).Count()}, + {"maxplayers", TShock.Config.MaxSlots}, + {"world", Main.worldName} + }; + + if (GetBool(parameters["players"], false)) + { + var players = new ArrayList(); + foreach (TSPlayer tsPlayer in TShock.Players.Where(p => null != p)) + { + var p = PlayerFilter(tsPlayer, parameters); + if (null != p) + players.Add(p); + } + ret.Add("players", players); + } + + if (GetBool(parameters["rules"], false)) + { + var rules = new Dictionary(); + rules.Add("AutoSave", TShock.Config.AutoSave); + rules.Add("DisableBuild", TShock.Config.DisableBuild); + rules.Add("DisableClownBombs", TShock.Config.DisableClownBombs); + rules.Add("DisableDungeonGuardian", TShock.Config.DisableDungeonGuardian); + rules.Add("DisableInvisPvP", TShock.Config.DisableInvisPvP); + rules.Add("DisableSnowBalls", TShock.Config.DisableSnowBalls); + rules.Add("DisableTombstones", TShock.Config.DisableTombstones); + rules.Add("EnableWhitelist", TShock.Config.EnableWhitelist); + rules.Add("HardcoreOnly", TShock.Config.HardcoreOnly); + rules.Add("PvPMode", TShock.Config.PvPMode); + rules.Add("SpawnProtection", TShock.Config.SpawnProtection); + rules.Add("SpawnProtectionRadius", TShock.Config.SpawnProtectionRadius); + + ret.Add("rules", rules); + } return ret; } + private object ServerTokenTest(RestVerbs verbs, IParameterCollection parameters) + { + return RestResponse("Token is valid and was passed through correctly"); + } + #endregion #region RestUserMethods + private object UserActiveListV2(RestVerbs verbs, IParameterCollection parameters) + { + return new RestObject() { { "activeusers", string.Join("\t", TShock.Players.Where(p => null != p && null != p.UserAccountName && p.Active).Select(p => p.UserAccountName)) } }; + } + private object UserListV2(RestVerbs verbs, IParameterCollection parameters) { - string playerlist = ""; - foreach (var TSPlayer in TShock.Players) + return new RestObject() { { "users", TShock.Users.GetUsers().Select(p => new Dictionary(){ + {"name", p.Name}, + {"id", p.ID}, + {"group", p.Group}, + {"ip", p.Address}, + }) } }; + } + + private object UserCreateV2(RestVerbs verbs, IParameterCollection parameters) + { + var username = parameters["user"]; + if (string.IsNullOrWhiteSpace(username)) + return RestMissingParam("user"); + + var group = parameters["group"]; + if (string.IsNullOrWhiteSpace(group)) + return RestMissingParam("group"); + + var password = parameters["password"]; + if (string.IsNullOrWhiteSpace(password)) + return RestMissingParam("password"); + + // NOTE: ip can be blank + User user = new User(parameters["ip"], username, password, group); + try { - if (TSPlayer == null) - { - continue; - } - playerlist += playerlist == "" ? TSPlayer.UserAccountName : "\t" + TSPlayer.UserAccountName; + TShock.Users.AddUser(user); } - var returnBlock = new Dictionary(); - returnBlock.Add("status", "200"); - returnBlock.Add("activeusers", playerlist); - return returnBlock; + catch (Exception e) + { + return RestError(e.Message); + } + + return RestResponse("User was successfully created"); } private object UserUpdateV2(RestVerbs verbs, IParameterCollection parameters) { - var returnBlock = new Dictionary(); + var ret = UserFind(parameters); + if (ret is RestObject) + return ret; + var password = parameters["password"]; var group = parameters["group"]; + if (string.IsNullOrWhiteSpace(group) && string.IsNullOrWhiteSpace(password)) + return RestMissingParam("group", "password"); - if (group == null && password == null) + User user = (User)ret; + var response = new RestObject(); + if (!string.IsNullOrWhiteSpace(password)) { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "No parameters were passed."); - return returnBlock; + try + { + TShock.Users.SetUserPassword(user, password); + response.Add("password-response", "Password updated successfully"); + } + catch (Exception e) + { + return RestError("Failed to update user password (" + e.Message + ")"); + } } - var user = TShock.Users.GetUserByName(parameters["user"]); - if (user == null) + if (!string.IsNullOrWhiteSpace(group)) { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "The specefied user doesn't exist."); - return returnBlock; + try + { + TShock.Users.SetUserGroup(user, group); + response.Add("group-response", "Group updated successfully"); + } + catch (Exception e) + { + return RestError("Failed to update user group (" + e.Message + ")"); + } } - if (password != null) - { - TShock.Users.SetUserPassword(user, password); - returnBlock.Add("password-response", "Password updated successfully."); - } - - if (group != null) - { - TShock.Users.SetUserGroup(user, group); - returnBlock.Add("group-response", "Group updated successfully."); - } - - returnBlock.Add("status", "200"); - return returnBlock; + return response; } private object UserDestroyV2(RestVerbs verbs, IParameterCollection parameters) { - var user = TShock.Users.GetUserByName(parameters["user"]); - if (user == null) - { - return new Dictionary {{"status", "400"}, {"error", "The specified user account does not exist."}}; - } - var returnBlock = new Dictionary(); + var ret = UserFind(parameters); + if (ret is RestObject) + return ret; + try { - TShock.Users.RemoveUser(user); + TShock.Users.RemoveUser((User)ret); } - catch (Exception) + catch (Exception e) { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "The specified user was unable to be removed."); - return returnBlock; + return RestError(e.Message); } - returnBlock.Add("status", "200"); - returnBlock.Add("response", "User deleted successfully."); - return returnBlock; + + return RestResponse("User deleted successfully"); } private object UserInfoV2(RestVerbs verbs, IParameterCollection parameters) { - var user = TShock.Users.GetUserByName(parameters["user"]); - if (user == null) - { - return new Dictionary {{"status", "400"}, {"error", "The specified user account does not exist."}}; - } + var ret = UserFind(parameters); + if (ret is RestObject) + return ret; - var returnBlock = new Dictionary(); - returnBlock.Add("status", "200"); - returnBlock.Add("group", user.Group); - returnBlock.Add("id", user.ID.ToString()); - return returnBlock; + User user = (User)ret; + return new RestObject() { { "group", user.Group }, { "id", user.ID.ToString() } }; } #endregion #region RestBanMethods - private object BanListIPs(RestVerbs verbs, IParameterCollection parameters) - { - RestObject returnItem = new RestObject("200"); - returnItem.Add("bans", TShock.Bans.GetBans()); - - return returnItem; - } - private object BanCreate(RestVerbs verbs, IParameterCollection parameters) { - var returnBlock = new Dictionary(); var ip = parameters["ip"]; var name = parameters["name"]; - var reason = parameters["reason"]; - if (ip == null && name == null) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Required parameters were missing from this API endpoint."); - return returnBlock; - } - - if (ip == null) - { - ip = ""; - } - - if (name == null) - { - name = ""; - } - - if (reason == null) - { - reason = ""; - } + if (string.IsNullOrWhiteSpace(ip) && string.IsNullOrWhiteSpace(name)) + return RestMissingParam("ip", "name"); try { - TShock.Bans.AddBan(ip, name, reason); + TShock.Bans.AddBan(ip, name, parameters["reason"], true); } - catch (Exception) + catch (Exception e) { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "The specified ban was unable to be created."); - return returnBlock; + return RestError(e.Message); } - returnBlock.Add("status", "200"); - returnBlock.Add("response", "Ban created successfully."); - return returnBlock; + return RestResponse("Ban created successfully"); } private object BanDestroyV2(RestVerbs verbs, IParameterCollection parameters) { - var returnBlock = new Dictionary(); - - var type = parameters["type"]; - if (type == null) - { - returnBlock.Add("Error", "Invalid Type"); - return returnBlock; - } - - var ban = new Ban(); - if (type == "ip") ban = TShock.Bans.GetBanByIp(parameters["user"]); - else if (type == "name") ban = TShock.Bans.GetBanByName(parameters["user"]); - else - { - returnBlock.Add("Error", "Invalid Type"); - return returnBlock; - } - - if (ban == null) - { - return new Dictionary {{"status", "400"}, {"error", "The specified ban does not exist."}}; - } + var ret = BanFind(parameters); + if (ret is RestObject) + return ret; try { - TShock.Bans.RemoveBan(ban.IP); + Ban ban = (Ban)ret; + switch (parameters["type"]) + { + case "ip": + if (!TShock.Bans.RemoveBan(ban.IP, false, false, true)) + return RestResponse("Failed to delete ban (already deleted?)"); + break; + case "name": + if (!TShock.Bans.RemoveBan(ban.Name, true, GetBool(parameters["caseinsensitive"], true))) + return RestResponse("Failed to delete ban (already deleted?)"); + break; + default: + return RestError("Invalid Type: '" + parameters["type"] + "'"); + } + } - catch (Exception) + catch (Exception e) { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "The specified ban was unable to be removed."); - return returnBlock; + return RestError(e.Message); } - returnBlock.Add("status", "200"); - returnBlock.Add("response", "Ban deleted successfully."); - return returnBlock; + + return RestResponse("Ban deleted successfully"); } private object BanInfoV2(RestVerbs verbs, IParameterCollection parameters) { - var returnBlock = new Dictionary(); + var ret = BanFind(parameters); + if (ret is RestObject) + return ret; - var type = parameters["type"]; - if (type == null) + Ban ban = (Ban)ret; + return new RestObject() { + {"name", null == ban.Name ? "" : ban.Name}, + {"ip", null == ban.IP ? "" : ban.IP}, + {"reason", null == ban.Reason ? "" : ban.Reason}, + }; + } + + private object BanListV2(RestVerbs verbs, IParameterCollection parameters) + { + var banList = new ArrayList(); + foreach (var ban in TShock.Bans.GetBans()) { - returnBlock.Add("Error", "Invalid Type"); - return returnBlock; + banList.Add( + new Dictionary + { + {"name", null == ban.Name ? "" : ban.Name}, + {"ip", null == ban.IP ? "" : ban.IP}, + {"reason", null == ban.Reason ? "" : ban.Reason}, + } + ); } - var ban = new Ban(); - if (type == "ip") ban = TShock.Bans.GetBanByIp(parameters["user"]); - else if (type == "name") ban = TShock.Bans.GetBanByName(parameters["user"]); - else - { - returnBlock.Add("Error", "Invalid Type"); - return returnBlock; - } - - if (ban == null) - { - return new Dictionary { { "status", "400" }, { "error", "The specified ban does not exist." } }; - } - - returnBlock.Add("status", "200"); - returnBlock.Add("name", ban.Name); - returnBlock.Add("ip", ban.IP); - returnBlock.Add("reason", ban.Reason); - return returnBlock; + return new RestObject() { { "bans", banList } }; } #endregion #region RestWorldMethods - private object ChangeWorldSaveSettings(RestVerbs verbs, IParameterCollection parameters) + private object WorldChangeSaveSettings(RestVerbs verbs, IParameterCollection parameters) { - bool state; - bool.TryParse(verbs["state"], out state); + bool autoSave; + if (!bool.TryParse(verbs["bool"], out autoSave)) + return RestInvalidParam("state"); + TShock.Config.AutoSave = autoSave; - if (state == true) - { - TShock.Config.AutoSave = true; - } - else - { - TShock.Config.AutoSave = false; - } - - RestObject rj = new RestObject("200"); - rj["response"] = "Value changed"; - rj["state"] = state; - - return rj; + return RestResponse("AutoSave has been set to " + autoSave); } private object WorldSave(RestVerbs verbs, IParameterCollection parameters) { TShock.Utils.SaveWorld(); - RestObject rj = new RestObject("200"); - rj["response"] = "World saved."; - return rj; + return RestResponse("World saved"); } - private object Butcher(RestVerbs verbs, IParameterCollection parameters) + private object WorldButcher(RestVerbs verbs, IParameterCollection parameters) { bool killFriendly; if (!bool.TryParse(parameters["killfriendly"], out killFriendly)) - { - RestObject fail = new RestObject("400"); - fail["response"] = "The given value for killfriendly wasn't a boolean value."; - return fail; - } + return RestInvalidParam("killfriendly"); + if (killFriendly) - { killFriendly = !killFriendly; - } int killcount = 0; for (int i = 0; i < Main.npc.Length; i++) @@ -436,54 +435,38 @@ namespace TShockAPI } } - RestObject rj = new RestObject("200"); - rj["response"] = killcount + " NPCs have been killed."; - return rj; + return RestResponse(killcount + " NPCs have been killed"); } private object WorldRead(RestVerbs verbs, IParameterCollection parameters) { - var returnBlock = new Dictionary(); - returnBlock.Add("status", "200"); - returnBlock.Add("name", Main.worldName); - returnBlock.Add("size", Main.maxTilesX + "*" + Main.maxTilesY); - returnBlock.Add("time", Main.time); - returnBlock.Add("daytime", Main.dayTime); - returnBlock.Add("bloodmoon", Main.bloodMoon); - returnBlock.Add("invasionsize", Main.invasionSize); - return returnBlock; + return new RestObject() + { + {"name", Main.worldName}, + {"size", Main.maxTilesX + "*" + Main.maxTilesY}, + {"time", Main.time}, + {"daytime", Main.dayTime}, + {"bloodmoon", Main.bloodMoon}, + {"invasionsize", Main.invasionSize} + }; } private object WorldMeteor(RestVerbs verbs, IParameterCollection parameters) { - if (WorldGen.genRand == null) + if (null == WorldGen.genRand) WorldGen.genRand = new Random(); WorldGen.dropMeteor(); - var returnBlock = new Dictionary {{"status", "200"}, {"response", "Meteor has been spawned."}}; - return returnBlock; + return RestResponse("Meteor has been spawned"); } private object WorldBloodmoon(RestVerbs verbs, IParameterCollection parameters) { - var returnBlock = new Dictionary(); - var bloodmoonVerb = verbs["bool"]; bool bloodmoon; - if (bloodmoonVerb == null) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "No parameter was passed."); - return returnBlock; - } - if (!bool.TryParse(bloodmoonVerb, out bloodmoon)) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Unable to parse parameter."); - return returnBlock; - } + if (!bool.TryParse(verbs["bool"], out bloodmoon)) + return RestInvalidParam("bloodmoon"); Main.bloodMoon = bloodmoon; - returnBlock.Add("status", "200"); - returnBlock.Add("response", "Blood Moon has been set to " + bloodmoon); - return returnBlock; + + return RestResponse("Blood Moon has been set to " + bloodmoon); } #endregion @@ -492,190 +475,342 @@ namespace TShockAPI private object PlayerUnMute(RestVerbs verbs, IParameterCollection parameters) { - var returnBlock = new Dictionary(); - var playerParam = parameters["player"]; - var found = TShock.Utils.FindPlayer(playerParam); - var reason = parameters["reason"]; - if (found.Count == 0) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " was not found"); - } - else if (found.Count > 1) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players"); - } - else if (found.Count == 1) - { - var player = found[0]; - player.mute = false; - player.SendMessage("You have been remotely unmuted."); - returnBlock.Add("status", "200"); - returnBlock.Add("response", "Player " + player.Name + " was muted."); - } - return returnBlock; + return PlayerSetMute(parameters, false); } private object PlayerMute(RestVerbs verbs, IParameterCollection parameters) { - var returnBlock = new Dictionary(); - var playerParam = parameters["player"]; - var found = TShock.Utils.FindPlayer(playerParam); - var reason = parameters["reason"]; - if (found.Count == 0) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " was not found"); - } - else if (found.Count > 1) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players"); - } - else if (found.Count == 1) - { - var player = found[0]; - player.mute = true; - player.SendMessage("You have been remotely muted."); - returnBlock.Add("status", "200"); - returnBlock.Add("response", "Player " + player.Name + " was muted."); - } - return returnBlock; - } - - private object PlayerListV2(RestVerbs verbs, IParameterCollection parameters) - { - RestObject returnBlock = new RestObject("200"); - returnBlock.Add("players", TShock.Players.Where(p => p != null && p.Active)); - return returnBlock; + return PlayerSetMute(parameters, true); } private object PlayerList(RestVerbs verbs, IParameterCollection parameters) { - var activeplayers = Main.player.Where(p => p != null && p.active).ToList(); - string currentPlayers = string.Join(", ", activeplayers.Select(p => p.name)); - var ret = new RestObject("200"); - ret["players"] = currentPlayers; - ret.Add("deprecated", "This endpoint is deprecated and has been replaced with /v2/lists/players."); - return ret; + var activeplayers = Main.player.Where(p => null != p && p.active).ToList(); + return new RestObject() { { "players", string.Join(", ", activeplayers.Select(p => p.name)) } }; + } + + private object PlayerListV2(RestVerbs verbs, IParameterCollection parameters) + { + var playerList = new ArrayList(); + foreach (TSPlayer tsPlayer in TShock.Players.Where(p => null != p)) + { + var p = PlayerFilter(tsPlayer, parameters); + if (null != p) + playerList.Add(p); + } + return new RestObject() { { "players", playerList } }; } private object PlayerReadV2(RestVerbs verbs, IParameterCollection parameters) { - var returnBlock = new Dictionary(); - var playerParam = parameters["player"]; - var found = TShock.Utils.FindPlayer(playerParam); - if (found.Count == 0) + var ret = PlayerFind(parameters); + if (ret is RestObject) + return ret; + + TSPlayer player = (TSPlayer)ret; + var activeItems = player.TPlayer.inventory.Where(p => p.active).ToList(); + return new RestObject() { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " was not found"); - } - else if (found.Count > 1) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players"); - } - else if (found.Count == 1) - { - var player = found[0]; - returnBlock.Add("status", "200"); - returnBlock.Add("nickname", player.Name); - returnBlock.Add("username", player.UserAccountName == null ? "" : player.UserAccountName); - returnBlock.Add("ip", player.IP); - returnBlock.Add("group", player.Group.Name); - returnBlock.Add("position", player.TileX + "," + player.TileY); - var activeItems = player.TPlayer.inventory.Where(p => p.active).ToList(); - returnBlock.Add("inventory", string.Join(", ", activeItems.Select(p => (p.name + ":" + p.stack)))); - returnBlock.Add("buffs", string.Join(", ", player.TPlayer.buffType)); - } - return returnBlock; + {"nickname", player.Name}, + {"username", null == player.UserAccountName ? "" : player.UserAccountName}, + {"ip", player.IP}, + {"group", player.Group.Name}, + {"position", player.TileX + "," + player.TileY}, + {"inventory", string.Join(", ", activeItems.Select(p => (p.name + ":" + p.stack)))}, + {"buffs", string.Join(", ", player.TPlayer.buffType)} + }; } private object PlayerKickV2(RestVerbs verbs, IParameterCollection parameters) { - var returnBlock = new Dictionary(); - var playerParam = parameters["player"]; - if (playerParam == null) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Unspecified name."); - return returnBlock; - } - var found = TShock.Utils.FindPlayer(playerParam); - var reason = parameters["reason"]; - if (found.Count == 0) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " was not found"); - } - else if (found.Count > 1) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players"); - } - else if (found.Count == 1) - { - var player = found[0]; - TShock.Utils.ForceKick(player, reason == null ? "Kicked via web" : reason); - returnBlock.Add("status", "200"); - returnBlock.Add("response", "Player " + player.Name + " was kicked"); - } - return returnBlock; + var ret = PlayerFind(parameters); + if (ret is RestObject) + return ret; + + TSPlayer player = (TSPlayer)ret; + TShock.Utils.ForceKick(player, null == parameters["reason"] ? "Kicked via web" : parameters["reason"]); + return RestResponse("Player " + player.Name + " was kicked"); } private object PlayerBanV2(RestVerbs verbs, IParameterCollection parameters) { - var returnBlock = new Dictionary(); - var playerParam = parameters["player"]; - var found = TShock.Utils.FindPlayer(playerParam); - var reason = parameters["reason"]; - if (found.Count == 0) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " was not found"); - } - else if (found.Count > 1) - { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players"); - } - else if (found.Count == 1) - { - var player = found[0]; - TShock.Bans.AddBan(player.IP, player.Name, reason == null ? "Banned via web" : reason); - TShock.Utils.ForceKick(player, reason == null ? "Banned via web" : reason); - returnBlock.Add("status", "200"); - returnBlock.Add("response", "Player " + player.Name + " was banned"); - } - return returnBlock; + var ret = PlayerFind(parameters); + if (ret is RestObject) + return ret; + + TSPlayer player = (TSPlayer)ret; + var reason = null == parameters["reason"] ? "Banned via web" : parameters["reason"]; + TShock.Bans.AddBan(player.IP, player.Name, reason); + TShock.Utils.ForceKick(player, reason); + return RestResponse("Player " + player.Name + " was banned"); } private object PlayerKill(RestVerbs verbs, IParameterCollection parameters) { - var returnBlock = new Dictionary(); - var playerParam = parameters["player"]; - var found = TShock.Utils.FindPlayer(playerParam); - var from = verbs["from"]; - if (found.Count == 0) + var ret = PlayerFind(parameters); + if (ret is RestObject) + return ret; + + TSPlayer player = (TSPlayer)ret; + player.DamagePlayer(999999); + var from = string.IsNullOrWhiteSpace(parameters["from"]) ? "Server Admin" : parameters["from"]; + player.SendMessage(string.Format("{0} just killed you!", from)); + return RestResponse("Player " + player.Name + " was killed"); + } + + #endregion + + #region RestGroupMethods + + private object GroupList(RestVerbs verbs, IParameterCollection parameters) + { + var groups = new ArrayList(); + foreach (Group group in TShock.Groups) { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " was not found"); + groups.Add(new Dictionary {{"name", group.Name}, {"parent", group.ParentName}, {"chatcolor", group.ChatColor}}); } - else if (found.Count > 1) + return new RestObject() { { "groups", groups } }; + } + + private object GroupInfo(RestVerbs verbs, IParameterCollection parameters) + { + var ret = GroupFind(parameters); + if (ret is RestObject) + return ret; + + Group group = (Group)ret; + return new RestObject() { + {"name", group.Name}, + {"parent", group.ParentName}, + {"chatcolor", group.ChatColor}, + {"permissions", group.permissions}, + {"negatedpermissions", group.negatedpermissions} + }; + } + + private object GroupDestroy(RestVerbs verbs, IParameterCollection parameters) + { + var ret = GroupFind(parameters); + if (ret is RestObject) + return ret; + + Group group = (Group)ret; + try { - returnBlock.Add("status", "400"); - returnBlock.Add("error", "Name " + playerParam + " matches " + playerParam.Count() + " players"); + TShock.Groups.DeleteGroup(group.Name, true); } - else if (found.Count == 1) + catch (Exception e) { - var player = found[0]; - player.DamagePlayer(999999); - player.SendMessage(string.Format("{0} just killed you!", from)); - returnBlock.Add("status", "200"); - returnBlock.Add("response", "Player " + player.Name + " was killed."); + return RestError(e.Message); } - return returnBlock; + + return RestResponse("Group '" + group.Name + "' deleted successfully"); + } + + private object GroupCreate(RestVerbs verbs, IParameterCollection parameters) + { + var name = parameters["group"]; + if (string.IsNullOrWhiteSpace(name)) + return RestMissingParam("group"); + try + { + TShock.Groups.AddGroup(name, parameters["parent"], parameters["permissions"], parameters["chatcolor"], true); + } + catch (Exception e) + { + return RestError(e.Message); + } + + return RestResponse("Group '" + name + "' created successfully"); + } + + private object GroupUpdate(RestVerbs verbs, IParameterCollection parameters) + { + var ret = GroupFind(parameters); + if (ret is RestObject) + return ret; + + Group group = (Group)ret; + var parent = (null == parameters["parent"]) ? group.ParentName : parameters["parent"]; + var chatcolor = (null == parameters["chatcolor"]) ? group.ChatColor : parameters["chatcolor"]; + var permissions = (null == parameters["permissions"]) ? group.Permissions : parameters["permissions"]; + try + { + TShock.Groups.UpdateGroup(group.Name, parent, permissions, chatcolor); + } + catch (Exception e) + { + return RestError(e.Message); + } + + return RestResponse("Group '" + group.Name + "' updated successfully"); + } + + #endregion + + #region Utility Methods + + private RestObject RestError(string message, string status = "400") + { + return new RestObject(status) {Error = message}; + } + + private RestObject RestResponse(string message, string status = "200") + { + return new RestObject(status) {Response = message}; + } + + private RestObject RestMissingParam(string var) + { + return RestError("Missing or empty " + var + " parameter"); + } + + private RestObject RestMissingParam(params string[] vars) + { + return RestMissingParam(string.Join(", ", vars)); + } + + private RestObject RestInvalidParam(string var) + { + return RestError("Missing or invalid " + var + " parameter"); + } + + private bool GetBool(string val, bool def) + { + bool ret; + return bool.TryParse(val, out ret) ? ret : def; + } + + private object PlayerFind(IParameterCollection parameters) + { + string name = parameters["player"]; + if (string.IsNullOrWhiteSpace(name)) + return RestMissingParam("player"); + + var found = TShock.Utils.FindPlayer(name); + switch(found.Count) + { + case 1: + return found[0]; + case 0: + return RestError("Player " + name + " was not found"); + default: + return RestError("Player " + name + " matches " + found.Count + " players"); + } + } + + private object UserFind(IParameterCollection parameters) + { + string name = parameters["user"]; + if (string.IsNullOrWhiteSpace(name)) + return RestMissingParam("user"); + + User user; + string type = parameters["type"]; + try + { + switch (type) + { + case null: + case "name": + type = "name"; + user = TShock.Users.GetUserByName(name); + break; + case "id": + user = TShock.Users.GetUserByID(Convert.ToInt32(name)); + break; + case "ip": + user = TShock.Users.GetUserByIP(name); + + break; + default: + return RestError("Invalid Type: '" + type + "'"); + } + } + catch (Exception e) + { + return RestError(e.Message); + } + + if (null == user) + return RestError(String.Format("User {0} '{1}' doesn't exist", type, name)); + + return user; + } + + private object BanFind(IParameterCollection parameters) + { + string name = parameters["ban"]; + if (string.IsNullOrWhiteSpace(name)) + return RestMissingParam("ban"); + + string type = parameters["type"]; + if (string.IsNullOrWhiteSpace(type)) + return RestMissingParam("type"); + + Ban ban; + switch (type) + { + case "ip": + ban = TShock.Bans.GetBanByIp(name); + break; + case "name": + ban = TShock.Bans.GetBanByName(name, GetBool(parameters["caseinsensitive"], true)); + break; + default: + return RestError("Invalid Type: '" + type + "'"); + } + + if (null == ban) + return RestError("Ban " + type + " '" + name + "' doesn't exist"); + + return ban; + } + + private object GroupFind(IParameterCollection parameters) + { + var name = parameters["group"]; + if (string.IsNullOrWhiteSpace(name)) + return RestMissingParam("group"); + + var group = TShock.Groups.GetGroupByName(name); + if (null == group) + return RestError("Group '" + name + "' doesn't exist"); + + return group; + } + + private Dictionary PlayerFilter(TSPlayer tsPlayer, IParameterCollection parameters) + { + var player = new Dictionary + { + {"nickname", tsPlayer.Name}, + {"username", null == tsPlayer.UserAccountName ? "" : tsPlayer.UserAccountName}, + {"ip", tsPlayer.IP}, + {"group", tsPlayer.Group.Name}, + {"active", tsPlayer.Active}, + {"state", tsPlayer.State}, + {"team", tsPlayer.Team}, + }; + foreach (IParameter filter in parameters) + { + if (player.ContainsKey(filter.Name) && !player[filter.Name].Equals(filter.Value)) + return null; + } + return player; + } + + private object PlayerSetMute(IParameterCollection parameters, bool mute) + { + var ret = PlayerFind(parameters); + if (ret is RestObject) + return ret; + + TSPlayer player = (TSPlayer)ret; + player.mute = mute; + var verb = mute ? "muted" : "unmuted"; + player.SendMessage("You have been remotely " + verb); + return RestResponse("Player " + player.Name + " was " + verb); } #endregion