diff --git a/CollabVMSharp/AccountLoginResult.cs b/CollabVMSharp/AccountLoginResult.cs new file mode 100644 index 0000000..c689c03 --- /dev/null +++ b/CollabVMSharp/AccountLoginResult.cs @@ -0,0 +1,7 @@ +namespace CollabVMSharp; + +public class AccountLoginResult +{ + public bool Success { get; set; } + public string? Error { get; set; } +} \ No newline at end of file diff --git a/CollabVMSharp/CollabVMClient.cs b/CollabVMSharp/CollabVMClient.cs index 8eb644d..2d36a80 100644 --- a/CollabVMSharp/CollabVMClient.cs +++ b/CollabVMSharp/CollabVMClient.cs @@ -41,11 +41,13 @@ public class CollabVMClient { private WebProxy? _proxy; private Dictionary> commandsSeparatedArgs; private Dictionary> commandsOneArg; + private bool _usesAccountAuth; // Tasks and related private TaskCompletionSource GotNodeList; private TaskCompletionSource GotConnectionToNode; private TaskCompletionSource GotTurn; private TaskCompletionSource GotStaff; + private TaskCompletionSource LoginResult; private List GotIPTasks; private SemaphoreSlim QEMUMonitorSemaphore; private TaskCompletionSource QEMUMonitorResult; @@ -61,6 +63,7 @@ public class CollabVMClient { public string Node { get { return this.node; } } public string Username { get { return this.username; } } + public bool UsesAccountAuth { get { return this._usesAccountAuth; } } // Events public event EventHandler Chat; public event EventHandler ChatHistory; @@ -78,6 +81,10 @@ public class CollabVMClient { public event EventHandler TurnUpdate; public event EventHandler ConnectionClosed; /// + /// Fired when the VM requires account authentication. The provided string is the authentication server base URL. + /// + public event EventHandler VMUsesAccountAuth; + /// /// Client for the CollabVM 1.x Server /// /// URL of the CollabVM Server to connect to (Should start with ws:// or wss://) @@ -99,6 +106,7 @@ public class CollabVMClient { this._perms = Permissions.None; this._connected = false; this._connectedToVM = false; + this._usesAccountAuth = false; this.framebuffer = new Image(1, 1); this.socket = new(); this.socket.Options.AddSubProtocol("guacamole"); @@ -126,6 +134,7 @@ public class CollabVMClient { this.GotConnectionToNode = new(); this.GotTurn = new(); this.GotStaff = new(); + this.LoginResult = new(); this.GotIPTasks = new(); this.QEMUMonitorResult = new(); this.QEMUMonitorSemaphore = new(1, 1); @@ -145,6 +154,7 @@ public class CollabVMClient { VoteCooldown += delegate { }; TurnUpdate += delegate { }; ConnectionClosed += delegate { }; + VMUsesAccountAuth += delegate { }; } /// /// Connect to the CollabVM Server @@ -162,11 +172,12 @@ public class CollabVMClient { this.SendMsg(Guacutils.Encode("rename", this.username)); else this.SendMsg(Guacutils.Encode("rename")); - if (this.node != null) - this.SendMsg(Guacutils.Encode("connect", this.node)); this.NOPRecieve.Start(); this.WebSocketLoop(); - return; + if (this.node == null) return; + this.GotConnectionToNode = new(); + this.SendMsg(Guacutils.Encode("connect", this.node)); + await this.GotConnectionToNode.Task; } private async void WebSocketLoop() { @@ -305,7 +316,11 @@ public class CollabVMClient { } default: { var user = _users.Find(u => u.Username == msgArr[2]); - user.Username = msgArr[3]; + // I've no clue why this check is now needed, this has never been a problem before, but now it's causing a NullReferenceException + if (user != null) + { + user.Username = msgArr[3]; + } this.UserRenamed.Invoke(this, new UserRenamedEventArgs { OldName = msgArr[2], NewName = msgArr[3], @@ -324,6 +339,7 @@ public class CollabVMClient { Username = msgArr[i], Rank = msgArr[i + 1] switch { "0" => Rank.Unregistered, + "1" => Rank.Registered, "2" => Rank.Admin, "3" => Rank.Moderator, _ => Rank.Unregistered @@ -411,6 +427,33 @@ public class CollabVMClient { this.TurnUpdate.Invoke(this, this._currentturn ); break; } + case "auth": + { + this._usesAccountAuth = true; + VMUsesAccountAuth.Invoke(this, msgArr[1]); + break; + } + case "login": + { + if (msgArr[1] == "1") + { + this._rank = Rank.Registered; + this.LoginResult.TrySetResult(new() + { + Success = true + }); + } + else + { + this.LoginResult.TrySetResult(new() + { + Success = false, + Error = msgArr[2] + }); + } + + break; + } case "admin": { switch (msgArr[1]) { case "0": { @@ -625,6 +668,7 @@ public class CollabVMClient { /// Password to log in with /// The rank received public async Task Login(string password) { + if (this._usesAccountAuth) throw new InvalidOperationException("This VM requires account authentication. Use the LoginAccount method instead."); this.GotStaff = new(); this.SendMsg(Guacutils.Encode("admin", "2", password)); return await this.GotStaff.Task; @@ -854,7 +898,14 @@ public class CollabVMClient { { if (newname == null) SendMsg(Guacutils.Encode("rename")); else SendMsg(Guacutils.Encode("rename", newname)); - } + } + + public async Task LoginAccount(string token) + { + this.LoginResult = new(); + this.SendMsg(Guacutils.Encode("login", token)); + return await this.LoginResult.Task; + } private void ProcessCommand(string username, string cmd) { // I stole this from stackoverflow diff --git a/CollabVMSharp/Permissions.cs b/CollabVMSharp/Permissions.cs index c2e0ab6..ddf6c2a 100644 --- a/CollabVMSharp/Permissions.cs +++ b/CollabVMSharp/Permissions.cs @@ -42,6 +42,7 @@ public class Permissions { public enum Rank { Unregistered = 0, + Registered = 1, Moderator = 3, Admin = 2 } \ No newline at end of file