diff --git a/Server/Command.cs b/Server/Command.cs index 277bf1a..cfe989b 100644 --- a/Server/Command.cs +++ b/Server/Command.cs @@ -142,7 +142,7 @@ namespace Server return true; } - public void ShowError() => Output.Error($"Usage: {CommandString}"); + public void ShowError() => Output.Error($"Usage: {CommandString}", true, false); } public static class Commands diff --git a/Server/Database.cs b/Server/Database.cs index d39039f..b2262db 100644 --- a/Server/Database.cs +++ b/Server/Database.cs @@ -252,7 +252,7 @@ namespace Server using (var reader = XmlReader.Create(DatabaseName)) { if (!Traverse(reader, MasterEntry)) return null; - while (reader.Read() && reader.NodeType != XmlNodeType.EndElement) + while ((reader.Name.Equals("User") && reader.NodeType != XmlNodeType.EndElement) || (reader.Read() && reader.NodeType != XmlNodeType.EndElement)) { if (reader.Name.Equals("User")) { @@ -308,6 +308,7 @@ namespace Server if (!l.Contains(entry) && p(entry)) l.Add(entry); + /* using (var reader = XmlReader.Create(DatabaseName)) { if (!Traverse(reader, MasterEntry)) return null; @@ -319,6 +320,24 @@ namespace Server if (e!=null && !l.Contains(e = FromEncoded(e)) && p(e)) l.Add(e); } } + */ + + using (var reader = XmlReader.Create(DatabaseName)) + { + if (!Traverse(reader, MasterEntry)) return null; + while ((reader.Name.Equals("User") && reader.NodeType != XmlNodeType.EndElement) || (reader.Read() && reader.NodeType != XmlNodeType.EndElement)) + { + if (reader.Name.Equals("User")) + { + User n = FromEncoded(User.Parse(ReadEntry(reader), this)); + if (n != null && p(n)) + { + if (!l.Contains(n)) l.Add(n); + } + } + } + } + return l.ToArray(); } @@ -362,6 +381,7 @@ namespace Server private static User FromEncoded(User entry) { + if (entry == null) return null; User u = new User(entry); u.Name = Decode(u.Name); foreach (var account in u.accounts) @@ -468,6 +488,28 @@ namespace Server History.Add(tx); return this; } + + public override string ToString() + { + StringBuilder builder = new StringBuilder(balance.ToString()); + foreach (var tx in History) + { + builder + .Append('{') + .Append(tx.from.ToBase64String()) + .Append('&') + .Append(tx.fromAccount.ToBase64String()) + .Append('&') + .Append(tx.to.ToBase64String()) + .Append('&') + .Append(tx.toAccount.ToBase64String()) + .Append('&') + .Append(tx.amount.ToString()); + if (tx.meta != null) builder.Append('&').Append(tx.meta.ToBase64String()); + builder.Append('}'); + } + return builder.ToString(); + } } public class User @@ -539,6 +581,9 @@ namespace Server { if (!e.Name.Equals("User")) return null; User user = new User(); + foreach (var attribute in e.Attributes) + if (attribute.Key.Equals("admin") && attribute.Value.Equals("true")) + user.IsAdministrator = true; foreach (var entry in e.NestedEntries) { if (entry.Name.Equals("Name")) user.Name = entry.Text; diff --git a/Server/Program.cs b/Server/Program.cs index f1941fa..e667b35 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using Tofvesson.Common; @@ -22,6 +23,10 @@ namespace Server // Initialize the database Database db = new Database("BankDB", "Resources"); + SetConsoleCtrlHandler(i => { + if (i == 2) db.Flush(); // Ensures that the database is flushed before the program exits + return false; + }, true); // Create a secure random provider and start getting RSA stuff CryptoRandomProvider random = new CryptoRandomProvider(); @@ -246,8 +251,46 @@ namespace Server // Possible errors: bad session id, bad account name, balance in account isn't 0 return ErrorResponse(id, (user==null? "badsession" : account==null? "badacc" : "hasbal")); } + user.accounts.Remove(account); + db.UpdateUser(user); // Update user info break; } + + case "Account_Get": + { + Database.User user = null; + Database.Account account = null; + if (!ParseDataPair(cmd[1], out string session, out string name) || // Get session id and account name + !GetUser(session, out user) || // Get user associated with session id + !GetAccount(name, user, out account) || + account.balance != 0) + { + // Don't print input data to output in case sensitive information was included + Output.Error($"Recieved problematic session id or account name!"); + + // Possible errors: bad session id, bad account name, balance in account isn't 0 + return ErrorResponse(id, (user == null ? "badsession" : account == null ? "badacc" : "hasbal")); + } + // Response example: "123.45{Sm9obiBEb2U=&Sm9obnMgQWNjb3VudA==&SmFuZSBEb2U=&SmFuZXMgQWNjb3VudA==&123.45&SGV5IHRoZXJlIQ==}" + // Exmaple data: balance=123.45, Transaction{to="John Doe", toAccount="Johns Account", from="Jane Doe", fromAccount="Janes Account", amount=123.45, meta="Hey there!"} + return GenerateResponse(id, account.ToString()); + } + case "Account_List": + { + // Get user + Database.User user = db.GetUser(cmd[1]); + if (user == null) return ErrorResponse(id, "baduser"); + + // Serialize account names + StringBuilder builder = new StringBuilder(); + + // Account names are serialized as base64 to prevent potential &'s in the names from causing unintended behaviour + foreach (var account in user.accounts) builder.Append(account.name.ToBase64String()).Append('&'); + if (builder.Length != 0) --builder.Length; + + // Return accounts + return GenerateResponse(id, builder); + } case "Reg": { if (!ParseDataPair(cmd[1], out string user, out string pass)) @@ -374,5 +417,10 @@ namespace Server server.StopRunning(); } + + // Handles unexpected console close events + private delegate bool EventHandler(int eventType); + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool SetConsoleCtrlHandler(EventHandler callback, bool add); } }