From 01a48c336d26034977fe003bb0d10c05a7eb0ca2 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Wed, 22 Sep 2021 12:58:05 +0200 Subject: [PATCH 01/82] New Connection Class replacing Client, Controller Class replaceing networkController, no json support yet --- PolyChat/Connection.cs | 102 ++++++++++++++++++++++++++++++++++ PolyChat/Controller.cs | 73 ++++++++++++++++++++++++ PolyChat/MainPage.xaml.cs | 43 +++++++++----- PolyChat/Package.appxmanifest | 6 +- PolyChat/PolyChat.csproj | 5 ++ 5 files changed, 213 insertions(+), 16 deletions(-) create mode 100644 PolyChat/Connection.cs create mode 100644 PolyChat/Controller.cs diff --git a/PolyChat/Connection.cs b/PolyChat/Connection.cs new file mode 100644 index 0000000..f32592c --- /dev/null +++ b/PolyChat/Connection.cs @@ -0,0 +1,102 @@ +using System; +using System.Diagnostics; +using EngineIOSharp.Common.Enum; +using Newtonsoft.Json.Linq; +using SocketIOSharp.Client; +using SocketIOSharp.Common; +using SocketIOSharp.Server; +using SocketIOSharp.Server.Client; + +namespace PolyChat +{ + public class Connection + { + private SocketIOClient Client; + private SocketIOSocket Socket; + private bool Connected = false; + + public Connection(string ip, ushort port, Action onMessage) + { + Debug.WriteLine("! CONNECTING TO SERVER !"); + // establish connection + Client = new SocketIOClient(new SocketIOClientOption(EngineIOScheme.http, ip, port)); + Client.Connect(); + // setup event listeners + Client.On(SocketIOEvent.CONNECTION, OnConnect); + Client.On(SocketIOEvent.DISCONNECT, OnDisconnect); + Client.On(SocketIOEvent.ERROR, (JToken[] Data) => OnError(Data)); + Client.On("message", (Action) onMessage); + } + + public Connection(ushort port, Action onMessage) + { + Debug.WriteLine("! SERVER STARTING !"); + SocketIOServer server = new SocketIOServer(new SocketIOServerOption( + port + )); + server.Start(); + Debug.WriteLine("Port " + server.Option.Port); + Debug.WriteLine("Path " + server.Option.Path); + // listen for connection + server.OnConnection((SocketIOSocket socket) => + { + Console.WriteLine("--- Client connected! ---"); + Socket = socket; + Connected = true; + // setup event listeners + Socket.On("input", (JToken[] data) => + { + Debug.WriteLine("--- Incoming input ---"); + onMessage(data); + socket.Emit("echo", data); + }); + Socket.On("message", (JToken[] data) => + { + Debug.WriteLine("--- Incoming message ---"); + onMessage(data); + socket.Emit("echo", data); + }); + Socket.On(SocketIOEvent.DISCONNECT, OnDisconnect); + Socket.On(SocketIOEvent.ERROR, (JToken[] Data) => OnError(Data)); + }); + } + public void SendMessage(string message) + { + Debug.WriteLine("--- Sending message ---"); + Debug.WriteLine($"Connected {Connected}"); + Debug.WriteLine($"Client {Client}"); + Debug.WriteLine($"Socket {Socket}"); + if (Socket != null) Socket.Emit("message", message); + else if (Client != null) Client.Emit("message", message); + } + + // Event Methods + + private void OnConnect() + { + Debug.WriteLine("--- Connection successfull ---"); + Connected = true; + } + private void OnDisconnect() + { + Debug.WriteLine("--- Disconnected! ---"); + Connected = false; + } + private void OnError(JToken[] data) + { + Debug.WriteLine("--- Error: ---"); + if (data != null && data.Length > 0 && data[0] != null) + Debug.WriteLine(data[0]); + else + Debug.WriteLine("Unkown Error"); + Debug.WriteLine("---"); + } + + // Getters + + public bool IsConnected() + { + return Connected; + } + } +} diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs new file mode 100644 index 0000000..a26cdbd --- /dev/null +++ b/PolyChat/Controller.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; +using System.Diagnostics; +using Newtonsoft.Json.Linq; +using System.Net; + +namespace PolyChat +{ + class Controller + { + // Constants + private const ushort PORT = 8050; + // Controller + private readonly MainPage UIController; + // Props + private Dictionary Connections = new Dictionary(); + private string OwnName = ""; + private string OwnIP; + + /// + /// Initializes Controller with UI access + /// + /// UWP UI Controller + public Controller(MainPage uiController) + { + UIController = uiController; + OwnIP = getIP(); + Serve(); + } + + public void Connect(string ip) + { + Debug.WriteLine("--- Controller.Connect ---"); + Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(Data))); + } + + private void Serve() + { + Debug.WriteLine("--- Controller.Serve ---"); + Connections.Add("unknownIP", new Connection(PORT, Data => OnMessage(Data))); + } + + public void SendMessage(string ip, string message) + { + Debug.WriteLine("--- Controller.SendMessage ---"); + Connections[ip].SendMessage(message); + } + + private void OnMessage(JToken[] data) + { + Debug.WriteLine("--- Controller.OnMessage ---"); + if (data != null && data.Length > 0 && data[0] != null) + { + Debug.WriteLine("Message: " + data[0]); + } + else Debug.WriteLine("Undefined: " + data); + } + + public string getIP() + { + IPHostEntry ipEntry = Dns.GetHostEntry(Dns.GetHostName()); + IPAddress[] addrList = ipEntry.AddressList; + + for (short i = 0; i < addrList.Length; i++) + { + if (addrList[i].ToString().Substring(0, 3).Equals("10.")) + { + return addrList[i].ToString(); + } + } + return null; + } + } +} diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 42f1ca2..ba84679 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -17,7 +17,7 @@ namespace PolyChat /// public sealed partial class MainPage : Page { - private NetworkingController networkingController; + private Controller Controller; private ObservableCollection Partners; private ChatPartner selectedPartner = null; private string username; @@ -25,9 +25,9 @@ namespace PolyChat { this.InitializeComponent(); // init controller - networkingController = new NetworkingController(this); + Controller = new Controller(this); // ui variables - ipAddress.Text = IP.GetCodeFromIP(networkingController.getIP().ToString()); + ipAddress.Text = IP.GetCodeFromIP(Controller.getIP()); Partners = new ObservableCollection(); updateNoChatsPlaceholder(); updateNoUsernamePlaceholder(); @@ -48,19 +48,10 @@ namespace PolyChat // EVENTS - public void OnChatPartnerSelected(object sender, RoutedEventArgs e) - { - string code = ((RadioButton)sender).Tag.ToString(); - selectedPartner = Partners.First(p => p.Code == code); - listViewMessages.ItemsSource = selectedPartner.Messages; - selectedPartnerName.Text = selectedPartner.Name; - updateNoChatSelected(); - } - public void OnSendMessage(object sender = null, RoutedEventArgs e = null) { selectedPartner.AddMessage(new Message(inputSend.Text,false)); - networkingController.sendMessage(selectedPartner.Code, inputSend.Text); + Controller.SendMessage(selectedPartner.Code, inputSend.Text); // clear input inputSend.Text = ""; } @@ -72,7 +63,7 @@ namespace PolyChat if (result == ContentDialogResult.Primary) { string ip = IP.GetIPfromCode(dialog.getValue()); - networkingController.connectNewClient(ip); + Controller.Connect(ip); Partners.Add(new ChatPartner( "Connecting...", ip @@ -94,6 +85,21 @@ namespace PolyChat updateNoUsernamePlaceholder(); } + /// + /// Adds a new ChatPartner to the UI with default Name. + /// + /// IP Adress, gets shown as Util.IP > Code + public void OnIncomingConnection(string ip) + { + Partners.Add(new ChatPartner( + "Connecting...", + ip + )); + } + /// + /// Adds an message to the UI, based on .sender if known + /// + /// ChatMessage public void OnIncomingMessage(Message message) { ChatPartner sendingPartner = Partners.First(p => p.Code == message.Ip); @@ -108,6 +114,15 @@ namespace PolyChat { Partners.Remove(selectedPartner); updateNoChatsPlaceholder(); + updateNoChatSelected(); + } + public void OnChatPartnerSelected(object sender, RoutedEventArgs e) + { + string code = ((RadioButton)sender).Tag.ToString(); + selectedPartner = Partners.First(p => p.Code == code); + listViewMessages.ItemsSource = selectedPartner.Messages; + selectedPartnerName.Text = selectedPartner.Name; + updateNoChatSelected(); } private void OnKeyUp(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e) diff --git a/PolyChat/Package.appxmanifest b/PolyChat/Package.appxmanifest index d4a461b..06228f6 100644 --- a/PolyChat/Package.appxmanifest +++ b/PolyChat/Package.appxmanifest @@ -4,7 +4,8 @@ xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" - IgnorableNamespaces="uap mp"> + xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3" + IgnorableNamespaces="uap mp uap3"> - + + \ No newline at end of file diff --git a/PolyChat/PolyChat.csproj b/PolyChat/PolyChat.csproj index 3410aba..5b6c905 100644 --- a/PolyChat/PolyChat.csproj +++ b/PolyChat/PolyChat.csproj @@ -119,6 +119,8 @@ App.xaml + + MainPage.xaml @@ -185,6 +187,9 @@ 6.2.12 + + 0.9.13 + 2.0.3 From f2202e6aa7a2d10707354030ea7685ea4674275a Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Wed, 22 Sep 2021 13:50:36 +0200 Subject: [PATCH 02/82] Added json support with type+content to ChatMessage --- PolyChat/Controller.cs | 1 - PolyChat/Models/ChatMessage.cs | 41 +++++++++++++++++++++++++--------- PolyChat/PolyChat.csproj | 4 ++++ 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index a26cdbd..d377df8 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -59,7 +59,6 @@ namespace PolyChat { IPHostEntry ipEntry = Dns.GetHostEntry(Dns.GetHostName()); IPAddress[] addrList = ipEntry.AddressList; - for (short i = 0; i < addrList.Length; i++) { if (addrList[i].ToString().Substring(0, 3).Equals("10.")) diff --git a/PolyChat/Models/ChatMessage.cs b/PolyChat/Models/ChatMessage.cs index 28cf47c..4c9f48f 100644 --- a/PolyChat/Models/ChatMessage.cs +++ b/PolyChat/Models/ChatMessage.cs @@ -1,29 +1,48 @@ using System; +using System.Diagnostics; +using System.Text.Json; namespace PolyChat.Models { public class ChatMessage { - public readonly string Sender; - public readonly DateTime Timestamp = new DateTime(1970, 01, 01); - public readonly string Content; - public readonly string Ip; + private string Origin; + private string Type; + private string Content; + private DateTime TimeStamp; public readonly bool Foreign; + // + public readonly string Ip; - public ChatMessage(string Content = "", bool Foreign = true, string Sender= "Unknown", string Ip = "127.0.0.1") + public ChatMessage(string content = "", string origin = "Unknown", string ip = "127.0.0.1") { - this.Sender = Sender; - this.Timestamp = DateTime.Now; - this.Content = Content; - this.Foreign = Foreign; - this.Ip = Ip; + Origin = origin; + TimeStamp = DateTime.Now; + Content = content; + Ip = ip; + // no json = my messages + Foreign = false; + Debug.WriteLine("Created Message: " + ToString()); + } + + public ChatMessage(string origin, string json) + { + Origin = origin; + // parse and save to object + var obj = JsonDocument.Parse(json).RootElement; + Type = obj.GetProperty("type").GetString(); + Content = obj.GetProperty("content").GetString(); + TimeStamp = DateTime.Now; + // json = foreign + Foreign = true; + Debug.WriteLine("Created Message: " + ToString()); } override public string ToString() { string prefix = Foreign ? "Other" : "Me"; - return $"{prefix}: {Content}({Sender})"; + return $"{Type} from {prefix}: {Content}({Origin})"; } } } \ No newline at end of file diff --git a/PolyChat/PolyChat.csproj b/PolyChat/PolyChat.csproj index 5b6c905..41da7e3 100644 --- a/PolyChat/PolyChat.csproj +++ b/PolyChat/PolyChat.csproj @@ -124,6 +124,7 @@ MainPage.xaml + @@ -193,6 +194,9 @@ 2.0.3 + + 5.0.2 + 14.0 From 3b2789ce542bd76ad56dde7935e17441e2825a02 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Wed, 22 Sep 2021 13:58:55 +0200 Subject: [PATCH 03/82] add initial packet, close socket on disconnect, move server start to Controller.cs --- PolyChat/Connection.cs | 52 +++++++++++++++++------------------------- PolyChat/Controller.cs | 26 ++++++++++++++++++--- 2 files changed, 44 insertions(+), 34 deletions(-) diff --git a/PolyChat/Connection.cs b/PolyChat/Connection.cs index f32592c..1fe0366 100644 --- a/PolyChat/Connection.cs +++ b/PolyChat/Connection.cs @@ -4,8 +4,8 @@ using EngineIOSharp.Common.Enum; using Newtonsoft.Json.Linq; using SocketIOSharp.Client; using SocketIOSharp.Common; -using SocketIOSharp.Server; using SocketIOSharp.Server.Client; +using PolyChat.Models; namespace PolyChat { @@ -14,10 +14,12 @@ namespace PolyChat private SocketIOClient Client; private SocketIOSocket Socket; private bool Connected = false; + private readonly string IP; public Connection(string ip, ushort port, Action onMessage) { Debug.WriteLine("! CONNECTING TO SERVER !"); + IP = ip; // establish connection Client = new SocketIOClient(new SocketIOClientOption(EngineIOScheme.http, ip, port)); Client.Connect(); @@ -28,37 +30,15 @@ namespace PolyChat Client.On("message", (Action) onMessage); } - public Connection(ushort port, Action onMessage) + public Connection(SocketIOSocket socket, Action onMessage) { - Debug.WriteLine("! SERVER STARTING !"); - SocketIOServer server = new SocketIOServer(new SocketIOServerOption( - port - )); - server.Start(); - Debug.WriteLine("Port " + server.Option.Port); - Debug.WriteLine("Path " + server.Option.Path); - // listen for connection - server.OnConnection((SocketIOSocket socket) => - { - Console.WriteLine("--- Client connected! ---"); - Socket = socket; - Connected = true; - // setup event listeners - Socket.On("input", (JToken[] data) => - { - Debug.WriteLine("--- Incoming input ---"); - onMessage(data); - socket.Emit("echo", data); - }); - Socket.On("message", (JToken[] data) => - { - Debug.WriteLine("--- Incoming message ---"); - onMessage(data); - socket.Emit("echo", data); - }); - Socket.On(SocketIOEvent.DISCONNECT, OnDisconnect); - Socket.On(SocketIOEvent.ERROR, (JToken[] Data) => OnError(Data)); - }); + Socket = socket; + Socket.On(SocketIOEvent.DISCONNECT, OnDisconnect); + Socket.On(SocketIOEvent.ERROR, (JToken[] Data) => OnError(Data)); + Socket.On("message", (Action)onMessage); + + //we are connected if we got here, inital packet was already received + Connected = true; } public void SendMessage(string message) { @@ -74,6 +54,8 @@ namespace PolyChat private void OnConnect() { + Debug.WriteLine("--- Sending initial packet to server ---"); + Client.Emit("initial", IP); Debug.WriteLine("--- Connection successfull ---"); Connected = true; } @@ -81,6 +63,7 @@ namespace PolyChat { Debug.WriteLine("--- Disconnected! ---"); Connected = false; + Close(); } private void OnError(JToken[] data) { @@ -90,10 +73,17 @@ namespace PolyChat else Debug.WriteLine("Unkown Error"); Debug.WriteLine("---"); + Close(); } // Getters + public void Close() + { + if (Client != null) Client.Close(); + if (Socket != null) Socket.Close(); + } + public bool IsConnected() { return Connected; diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index d377df8..b973258 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -2,6 +2,8 @@ using System.Diagnostics; using Newtonsoft.Json.Linq; using System.Net; +using SocketIOSharp.Server; +using SocketIOSharp.Server.Client; namespace PolyChat { @@ -30,13 +32,31 @@ namespace PolyChat public void Connect(string ip) { Debug.WriteLine("--- Controller.Connect ---"); - Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(Data))); + new Connection(ip, PORT, Data => OnMessage(Data)); } private void Serve() { - Debug.WriteLine("--- Controller.Serve ---"); - Connections.Add("unknownIP", new Connection(PORT, Data => OnMessage(Data))); + Debug.WriteLine("! SERVER STARTING !"); + SocketIOServer server = new SocketIOServer(new SocketIOServerOption( + PORT + )); + server.Start(); + Debug.WriteLine("Port " + server.Option.Port); + Debug.WriteLine("Path " + server.Option.Path); + // listen for connection + server.OnConnection((SocketIOSocket socket) => + { + Debug.WriteLine("--- Client connected! ---"); + // setup event listeners + socket.On("initial", (JToken[] data) => + { + Debug.WriteLine("--- initial packet received ---"); + string ForeignIp = data.ToString(); + //Todo deserialize inital packet and extract ip address + Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(Data))); + }); + }); } public void SendMessage(string ip, string message) From 0c5579198f3a40a02c1893a5c91962cee5f16f86 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Wed, 22 Sep 2021 14:09:26 +0200 Subject: [PATCH 04/82] Removed Old Models, extended ChatMessage to json --- PolyChat/Controller.cs | 1 + PolyChat/MainPage.xaml | 6 +- PolyChat/MainPage.xaml.cs | 12 +- PolyChat/Models/ChatMessage.cs | 14 +-- PolyChat/Models/ChatPartner.cs | 8 +- PolyChat/Models/Client.cs | 144 ------------------------ PolyChat/{ => Models}/Connection.cs | 2 +- PolyChat/Models/MSG.cs | 29 ----- PolyChat/Models/Message.cs | 55 --------- PolyChat/Models/NetworkingController.cs | 126 --------------------- PolyChat/Models/SendCode.cs | 10 -- PolyChat/PolyChat.csproj | 6 +- 12 files changed, 20 insertions(+), 393 deletions(-) delete mode 100644 PolyChat/Models/Client.cs rename PolyChat/{ => Models}/Connection.cs (99%) delete mode 100644 PolyChat/Models/MSG.cs delete mode 100644 PolyChat/Models/Message.cs delete mode 100644 PolyChat/Models/NetworkingController.cs delete mode 100644 PolyChat/Models/SendCode.cs diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index d377df8..43f856e 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using Newtonsoft.Json.Linq; using System.Net; +using PolyChat.Models; namespace PolyChat { diff --git a/PolyChat/MainPage.xaml b/PolyChat/MainPage.xaml index 5484d94..ebdfd13 100644 --- a/PolyChat/MainPage.xaml +++ b/PolyChat/MainPage.xaml @@ -99,10 +99,10 @@ - + - - + + diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index ba84679..67f6688 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -50,7 +50,7 @@ namespace PolyChat public void OnSendMessage(object sender = null, RoutedEventArgs e = null) { - selectedPartner.AddMessage(new Message(inputSend.Text,false)); + selectedPartner.AddMessage(new ChatMessage(username, "message" , inputSend.Text)); Controller.SendMessage(selectedPartner.Code, inputSend.Text); // clear input inputSend.Text = ""; @@ -100,14 +100,10 @@ namespace PolyChat /// Adds an message to the UI, based on .sender if known /// /// ChatMessage - public void OnIncomingMessage(Message message) + public void OnIncomingMessage(string origin, string json) { - ChatPartner sendingPartner = Partners.First(p => p.Code == message.Ip); - sendingPartner.AddMessage(new Message( - message.Msg, - true, - message.Sender - )); + ChatPartner sendingPartner = Partners.First(p => p.Code == origin); + sendingPartner.AddMessage(new ChatMessage(origin, json)); } private void OnDeleteChat(object sender = null, RoutedEventArgs e = null) diff --git a/PolyChat/Models/ChatMessage.cs b/PolyChat/Models/ChatMessage.cs index 4c9f48f..30b3ea2 100644 --- a/PolyChat/Models/ChatMessage.cs +++ b/PolyChat/Models/ChatMessage.cs @@ -6,20 +6,18 @@ namespace PolyChat.Models { public class ChatMessage { - private string Origin; - private string Type; - private string Content; - private DateTime TimeStamp; + public string Origin; + public string Type; + public string Content; + public DateTime TimeStamp; public readonly bool Foreign; // - public readonly string Ip; - - public ChatMessage(string content = "", string origin = "Unknown", string ip = "127.0.0.1") + public ChatMessage(string origin, string type, string content) { Origin = origin; TimeStamp = DateTime.Now; + Type = type; Content = content; - Ip = ip; // no json = my messages Foreign = false; Debug.WriteLine("Created Message: " + ToString()); diff --git a/PolyChat/Models/ChatPartner.cs b/PolyChat/Models/ChatPartner.cs index 8b93455..020433d 100644 --- a/PolyChat/Models/ChatPartner.cs +++ b/PolyChat/Models/ChatPartner.cs @@ -7,20 +7,20 @@ namespace PolyChat.Models { public string Name; public string Code; - public ObservableCollection Messages; + public ObservableCollection Messages; private SocketIOSocket socketIOSocket; - public ChatPartner(string name, string code, ObservableCollection messages = null) + public ChatPartner(string name, string code, ObservableCollection messages = null) { Name = name; Code = code; - if (messages == null) Messages = new ObservableCollection(); + if (messages == null) Messages = new ObservableCollection(); else Messages = messages; } public SocketIOSocket SocketIOSocket { get => socketIOSocket; set => socketIOSocket = value; } - public void AddMessage(Message message) + public void AddMessage(ChatMessage message) { Messages.Add(message); } diff --git a/PolyChat/Models/Client.cs b/PolyChat/Models/Client.cs deleted file mode 100644 index daf0256..0000000 --- a/PolyChat/Models/Client.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System; -using SocketIOSharp.Common; -using SocketIOSharp.Client; -using SocketIOSharp.Server.Client; -using Json.Net; -using System.Threading; -using PolyChat.Models.Exceptions; -using System.Diagnostics; - -namespace PolyChat.Models -{ - class Client - { - private SocketIOClient connection_client = null; - private SocketIOSocket connection_server = null; - private Boolean connected = true; - private String ipSelf; - - public Client(SocketIOClient connection, String ip, MainPage uiController) - { - this.ipSelf = ip; - this.connection_client = connection; - InitEventHandlers(this, connection, uiController); - } - - public Client(SocketIOSocket connection, String ip, MainPage uiController) - { - this.ipSelf = ip; - this.connection_server = connection; - InitEventHandlers(this, connection, uiController); - } - - //Sending - //=================================================================================== - /// - /// converts String message into json file and sends it to the server. - /// - /// - /// gets called by gui if someone wants to send Message - /// - /// Sender of Message - /// the accual text the user wants to send - /// current time - public void sendMessage(SendCode code, String chatMessage) - { - new Thread(() => - { - Debug.WriteLine($"connected is {connected}"); - //create msg - Message msg = new Message(chatMessage, false, ipSelf); - - //convert msg - String petJson = JsonNet.Serialize(msg); - - //wait if not connected and send msg - int i=0; - int sleeptimer = 2000; - while(!this.connected) - { - Thread.Sleep(sleeptimer); - i++; - if(i>=10) - { - throw new MessageTimedOutException(i*sleeptimer); - } - } - if (connection_client != null) - { - connection_client.Emit(code.ToString(), petJson); - }else if (connection_server != null) - { - connection_server.Emit(code.ToString(), petJson); - } - }).Start(); - } - - //================================================================================== - //EventHandeling - //=================================================================================== - - /// - /// handles all events of client - /// - /// self - /// - private static void InitEventHandlers(Client client, SocketIOClient connection, MainPage uiController) - { - connection.On(SendCode.Message.ToString(), (Data) => - { - Message msg = new Message(Data[0]); - uiController.OnIncomingMessage(msg); - - //TODO: send message to GUI - }); - - connection.On(SendCode.Command.ToString(), (Data) => - { - Console.WriteLine("Command recieved!" + Data[0]); - - }); - - connection.On(SocketIOEvent.CONNECTION, () => - { - client.connected = true; - }); - } - - /// - /// handles all events of server - /// - /// self - /// - private static void InitEventHandlers(Client client, SocketIOSocket connection, MainPage uiController) - { - connection.On(SendCode.Message.ToString(), (Data) => - { - Message msg = new Message(Data[0]); - uiController.OnIncomingMessage(msg); - //TODO: send message to GUI - }); - - connection.On(SendCode.Command.ToString(), (Data) => - { - Console.WriteLine("Command recieved!" + Data[0]); - }); - - client.connected = true; - } - //================================================================================== - //Getter and Setter - //================================================================================== - - public String getIP() - { - return this.ipSelf; - } - - public Boolean isConnected() - { - return this.connected; - } - } - -} diff --git a/PolyChat/Connection.cs b/PolyChat/Models/Connection.cs similarity index 99% rename from PolyChat/Connection.cs rename to PolyChat/Models/Connection.cs index f32592c..32daad4 100644 --- a/PolyChat/Connection.cs +++ b/PolyChat/Models/Connection.cs @@ -7,7 +7,7 @@ using SocketIOSharp.Common; using SocketIOSharp.Server; using SocketIOSharp.Server.Client; -namespace PolyChat +namespace PolyChat.Models { public class Connection { diff --git a/PolyChat/Models/MSG.cs b/PolyChat/Models/MSG.cs deleted file mode 100644 index 637fc3b..0000000 --- a/PolyChat/Models/MSG.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Text; -using System.Threading.Tasks; - -namespace PolyChat.Models -{ - /// - /// dumy class for json converter - /// - public class MSG - { - public String sender = "unknown"; - public DateTime timestamp = new DateTime(2000, 01, 01); - public String msg = "empty"; - public IPAddress ip = new IPAddress(new byte[] { 49,48,46,49,46,50,49,49,46,50,54 }); - - - public MSG(IPAddress ip, String msg, DateTime timestamp) - { - this.sender = sender; - this.ip = ip; - this.timestamp = timestamp; - this.msg = msg; - } - } -} diff --git a/PolyChat/Models/Message.cs b/PolyChat/Models/Message.cs deleted file mode 100644 index 6e01ea2..0000000 --- a/PolyChat/Models/Message.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Newtonsoft.Json.Linq; -using System; - -namespace PolyChat.Models -{ - public class Message - { - public readonly string Sender; - public readonly DateTime Timestamp = new DateTime(1970, 01, 01); - public readonly string Msg = "empty"; - public readonly string Ip; - public readonly bool Foreign; - public readonly string StringTimeStamp; - - /// - /// create new Message object from parameters - /// - /// - /// - /// - /// - public Message(string Msg = "", bool Foreign = true, string Sender= "Unknown", string Ip = "127.0.0.1") - { - this.Sender = Sender; - this.Timestamp = DateTime.Now; - StringTimeStamp = Timestamp.ToString(); - this.Msg = Msg; - this.Foreign = Foreign; - this.Ip = Ip; - } - - /// - /// create new Message object from JToken (json) - /// - /// - public Message(JToken data) - { - Message m = (Message) data[0].ToObject(); - Sender = m.Sender; - Timestamp = m.Timestamp; - StringTimeStamp = Timestamp.ToString(); - Msg = m.Msg; - Ip = m.Ip; - Foreign = m.Foreign; - } - - - override - public string ToString() - { - string prefix = Foreign ? "Other" : "Me"; - return $"{prefix}: {Msg}({Sender})"; - } - } -} \ No newline at end of file diff --git a/PolyChat/Models/NetworkingController.cs b/PolyChat/Models/NetworkingController.cs deleted file mode 100644 index d938d50..0000000 --- a/PolyChat/Models/NetworkingController.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Diagnostics; -using System.Collections.Generic; -using SocketIOSharp.Client; -using EngineIOSharp.Common.Enum; -using System.Net; -using PolyChat.Models.Exceptions; - -//dependencies for server functionality -using SocketIOSharp.Server; -using SocketIOSharp.Server.Client; -using Newtonsoft.Json.Linq; -using System.Threading; - -namespace PolyChat.Models -{ - class NetworkingController - { - public List clients = new List(); - private String ownName = ""; - private IPAddress ownIP; - private readonly ushort Port; - private SocketIOServer Server; - private readonly MainPage uiController; - - public NetworkingController (MainPage uiController, ushort Port = 8050) - { - this.uiController = uiController; - this.Port = Port; - ownIP = getIP(); - startServer(); - } - - //EXTERNAL METHODS - //========================================================================================================================================================================================= - - /// - /// connects self to server with given ip - /// - /// server to connect to - public void connectNewClient(String ip) - { - SocketIOClient connection = new SocketIOClient(new SocketIOClientOption(EngineIOScheme.http, ip, 8050)); - connection.Connect(); - clients.Add(new Client(connection, ip, uiController)); - } - - /// - /// handle incomming connection - /// - /// server to connect to - private void connectNewClient(SocketIOSocket socket) - { - socket.On(SendCode.Initial.ToString(), (JToken[] Data) => - { - Debug.WriteLine("Client connected!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); - Message m = new Message(Data[0]); - clients.Add(new Client(socket,m.Ip, uiController)); - }); - } - - /// - /// sends Message to given ip - /// - /// partner to send to - /// to send - public void sendMessage(String ip, String msg) - { - this.getClient(ip).sendMessage(SendCode.Initial, msg); - } - - /// - /// returns own ip adress - /// - /// - public IPAddress getIP() - { - IPHostEntry ipEntry = Dns.GetHostEntry(Dns.GetHostName()); - IPAddress[] addrList = ipEntry.AddressList; - - for (short i = 0; i < addrList.Length; i++) - { - if (addrList[i].ToString().Substring(0, 3).Equals("10.")) - { - return addrList[i]; - } - } - return null; - } - - public MainPage getUIController() - { - return this.uiController; - } - - - //========================================================================================================================================================================================= - //INTERNAL METHODS - //========================================================================================================================================================================================= - private void startServer() - { - Server = new SocketIOServer(new SocketIOServerOption(Port)); - Server.OnConnection((socket) => connectNewClient(socket)); - Server.Start(); - Debug.WriteLine($"Your ip is: {ownIP}"); - Debug.WriteLine($"Server started, binding to port {Port}, waiting for connection..."); - } - - /// - /// returns client that fit to ip address - /// - /// - /// - private Client getClient(String ip) - { - foreach (Client cl in clients) - { - if (cl.getIP().Equals(ip)) - { - return cl; - } - } - return null; - } - } -} diff --git a/PolyChat/Models/SendCode.cs b/PolyChat/Models/SendCode.cs deleted file mode 100644 index 0995534..0000000 --- a/PolyChat/Models/SendCode.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace PolyChat.Models -{ - enum SendCode - { - Message, - Command, - NameChange, - Initial - } -} \ No newline at end of file diff --git a/PolyChat/PolyChat.csproj b/PolyChat/PolyChat.csproj index 41da7e3..3f479cf 100644 --- a/PolyChat/PolyChat.csproj +++ b/PolyChat/PolyChat.csproj @@ -119,19 +119,15 @@ App.xaml - + MainPage.xaml - - - - From cadb1e5ecab05fcf57e6f0a918a25a80b2722a75 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Wed, 22 Sep 2021 14:28:43 +0200 Subject: [PATCH 05/82] add outgoing connections to dict --- PolyChat/Controller.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 48dd274..8b03a2c 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -33,7 +33,7 @@ namespace PolyChat public void Connect(string ip) { Debug.WriteLine("--- Controller.Connect ---"); - new Connection(ip, PORT, Data => OnMessage(Data)); + Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(Data))); } private void Serve() From 3b2b75df537a9070c942d155b2507036708e98d7 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Wed, 22 Sep 2021 14:57:36 +0200 Subject: [PATCH 06/82] SendMessage is now Json --- PolyChat/Controller.cs | 12 +++++++++--- PolyChat/MainPage.xaml.cs | 17 +++++++++++------ PolyChat/Models/Connection.cs | 2 +- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 48dd274..33d29dc 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -5,6 +5,7 @@ using System.Net; using SocketIOSharp.Server; using SocketIOSharp.Server.Client; using PolyChat.Models; +using System.Text.Json; namespace PolyChat { @@ -50,20 +51,24 @@ namespace PolyChat { Debug.WriteLine("--- Client connected! ---"); // setup event listeners - socket.On("initial", (JToken[] data) => + socket.On("initial", async (JToken[] data) => { Debug.WriteLine("--- initial packet received ---"); string ForeignIp = data.ToString(); //Todo deserialize inital packet and extract ip address Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(Data))); + UIController.OnIncomingConnection(ForeignIp); }); }); } - public void SendMessage(string ip, string message) + public void SendMessage(string ip, string type, string content) { Debug.WriteLine("--- Controller.SendMessage ---"); - Connections[ip].SendMessage(message); + Debug.WriteLine($"{type} -> {ip} content: {content}"); + string json = $"{{ type: {type}, content: {content} }}"; + Debug.WriteLine($"json: {json}"); + Connections[ip].SendMessage(json); } private void OnMessage(JToken[] data) @@ -72,6 +77,7 @@ namespace PolyChat if (data != null && data.Length > 0 && data[0] != null) { Debug.WriteLine("Message: " + data[0]); + Debug.WriteLine($"DATA: {data[0].ToString()}"); } else Debug.WriteLine("Undefined: " + data); } diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 67f6688..8f51424 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -5,6 +5,7 @@ using System; using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; +using Windows.UI.Core; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -51,7 +52,7 @@ namespace PolyChat public void OnSendMessage(object sender = null, RoutedEventArgs e = null) { selectedPartner.AddMessage(new ChatMessage(username, "message" , inputSend.Text)); - Controller.SendMessage(selectedPartner.Code, inputSend.Text); + Controller.SendMessage(selectedPartner.Code, "message", inputSend.Text); // clear input inputSend.Text = ""; } @@ -89,12 +90,16 @@ namespace PolyChat /// Adds a new ChatPartner to the UI with default Name. /// /// IP Adress, gets shown as Util.IP > Code - public void OnIncomingConnection(string ip) + public async void OnIncomingConnection(string ip) { - Partners.Add(new ChatPartner( - "Connecting...", - ip - )); + await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + Partners.Add(new ChatPartner( + "Connecting...", + ip + )); + updateNoChatsPlaceholder(); + }); } /// /// Adds an message to the UI, based on .sender if known diff --git a/PolyChat/Models/Connection.cs b/PolyChat/Models/Connection.cs index f214739..6700330 100644 --- a/PolyChat/Models/Connection.cs +++ b/PolyChat/Models/Connection.cs @@ -35,7 +35,7 @@ namespace PolyChat.Models Socket = socket; Socket.On(SocketIOEvent.DISCONNECT, OnDisconnect); Socket.On(SocketIOEvent.ERROR, (JToken[] Data) => OnError(Data)); - Socket.On("message", (Action)onMessage); + Socket.On("message", (Action) onMessage); //we are connected if we got here, inital packet was already received Connected = true; From ee7af3c98424bbc615295d47344672c7f2c64204 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Wed, 22 Sep 2021 15:14:25 +0200 Subject: [PATCH 07/82] Added our default ip address, added ip as param to onMessage --- PolyChat/Controller.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 946b408..2d9c8f7 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -29,12 +29,16 @@ namespace PolyChat UIController = uiController; OwnIP = getIP(); Serve(); + + //Connect("10.1.211.26"); // Marc + //Connect("10.1.218.90"); // Felix + //Connect("10.4.141.77"); // Pat } public void Connect(string ip) { Debug.WriteLine("--- Controller.Connect ---"); - Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(Data))); + Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(ip, Data))); } private void Serve() @@ -56,7 +60,7 @@ namespace PolyChat Debug.WriteLine("--- initial packet received ---"); string ForeignIp = data.ToString(); //Todo deserialize inital packet and extract ip address - Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(Data))); + Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data))); UIController.OnIncomingConnection(ForeignIp); }); }); @@ -71,13 +75,14 @@ namespace PolyChat Connections[ip].SendMessage(json); } - private void OnMessage(JToken[] data) + private void OnMessage(string ip, JToken[] data) { Debug.WriteLine("--- Controller.OnMessage ---"); if (data != null && data.Length > 0 && data[0] != null) { Debug.WriteLine("Message: " + data[0]); - Debug.WriteLine($"DATA: {data[0].ToString()}"); + Debug.WriteLine($"RAW: {data[0].ToString()}"); + UIController.OnIncomingMessage(ip, data[0].ToString()); } else Debug.WriteLine("Undefined: " + data); } From 957644b8417418c4cbc225615b53d91e639c0a7a Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Wed, 22 Sep 2021 15:50:19 +0200 Subject: [PATCH 08/82] Fixed json formatting in SendMessage --- PolyChat/Controller.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 2d9c8f7..835a540 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -6,6 +6,7 @@ using SocketIOSharp.Server; using SocketIOSharp.Server.Client; using PolyChat.Models; using System.Text.Json; +using Newtonsoft.Json; namespace PolyChat { @@ -43,7 +44,7 @@ namespace PolyChat private void Serve() { - Debug.WriteLine("! SERVER STARTING !"); + Debug.WriteLine("--- Controller.Serve ---"); SocketIOServer server = new SocketIOServer(new SocketIOServerOption( PORT )); @@ -70,9 +71,12 @@ namespace PolyChat { Debug.WriteLine("--- Controller.SendMessage ---"); Debug.WriteLine($"{type} -> {ip} content: {content}"); - string json = $"{{ type: {type}, content: {content} }}"; - Debug.WriteLine($"json: {json}"); - Connections[ip].SendMessage(json); + JObject json = new JObject( + new JProperty("type", type), + new JProperty("content", content) + ); + Debug.WriteLine($"json: {json.ToString()}"); + Connections[ip].SendMessage(json.ToString()); } private void OnMessage(string ip, JToken[] data) From 99dad406eae6793766a456acf0d6ebdf4352a2b2 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Wed, 22 Sep 2021 15:51:53 +0200 Subject: [PATCH 09/82] add retry logic on disconnect, silently delete chat if disconnected after connect was succesfull --- PolyChat/Controller.cs | 13 +++++++++++-- PolyChat/MainPage.xaml.cs | 33 ++++++++++++++++++++++++++------- PolyChat/Models/Connection.cs | 12 ++++++++---- 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 835a540..0c3bf59 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -39,7 +39,7 @@ namespace PolyChat public void Connect(string ip) { Debug.WriteLine("--- Controller.Connect ---"); - Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(ip, Data))); + Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(ip, Data), CloseChat)); } private void Serve() @@ -61,7 +61,7 @@ namespace PolyChat Debug.WriteLine("--- initial packet received ---"); string ForeignIp = data.ToString(); //Todo deserialize inital packet and extract ip address - Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data))); + Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data), CloseChat)); UIController.OnIncomingConnection(ForeignIp); }); }); @@ -91,6 +91,15 @@ namespace PolyChat else Debug.WriteLine("Undefined: " + data); } + public void CloseChat(string IP, bool wasConnected = true) + { + Connections[IP].Close(); + Connections.Remove(IP); + UIController.OnChatPartnerDeleted(IP); + if(!wasConnected) + UIController.ShowConnectionError(IP, $"Connection to {IP} failed..."); + } + public string getIP() { IPHostEntry ipEntry = Dns.GetHostEntry(Dns.GetHostName()); diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 8f51424..0dc0f43 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -36,15 +36,22 @@ namespace PolyChat updateSendButtonEnabled(); } - public async void ShowConnectionError(string message) + public async void ShowConnectionError(string code, string message) { - ConnectionFailedDialog dialog = new ConnectionFailedDialog(message); - var result = await dialog.ShowAsync(); - if (result == ContentDialogResult.Primary) + await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => { - //retry - } - // else abort -> del chat + ConnectionFailedDialog dialog = new ConnectionFailedDialog(message); + var result = await dialog.ShowAsync(); + if (result == ContentDialogResult.Primary) + { + Controller.Connect(code); + Partners.Add(new ChatPartner( + "Connecting...", + code + )); + updateNoChatsPlaceholder(); + } + }); } // EVENTS @@ -113,10 +120,22 @@ namespace PolyChat private void OnDeleteChat(object sender = null, RoutedEventArgs e = null) { + Controller.CloseChat(selectedPartner.Code); Partners.Remove(selectedPartner); updateNoChatsPlaceholder(); updateNoChatSelected(); } + + public async void OnChatPartnerDeleted(string code) + { + await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + Partners.Remove(Partners.First(p => p.Code == code)); + selectedPartner = null; + updateNoChatsPlaceholder(); + updateNoChatSelected(); + }); + } public void OnChatPartnerSelected(object sender, RoutedEventArgs e) { string code = ((RadioButton)sender).Tag.ToString(); diff --git a/PolyChat/Models/Connection.cs b/PolyChat/Models/Connection.cs index 6700330..971e6af 100644 --- a/PolyChat/Models/Connection.cs +++ b/PolyChat/Models/Connection.cs @@ -15,11 +15,13 @@ namespace PolyChat.Models private SocketIOSocket Socket; private bool Connected = false; private readonly string IP; + private Action DeleteConnection; - public Connection(string ip, ushort port, Action onMessage) + public Connection(string ip, ushort port, Action onMessage, Action onClose) { Debug.WriteLine("! CONNECTING TO SERVER !"); IP = ip; + DeleteConnection = onClose; // establish connection Client = new SocketIOClient(new SocketIOClientOption(EngineIOScheme.http, ip, port)); Client.Connect(); @@ -29,10 +31,11 @@ namespace PolyChat.Models Client.On(SocketIOEvent.ERROR, (JToken[] Data) => OnError(Data)); Client.On("message", (Action) onMessage); } - - public Connection(SocketIOSocket socket, Action onMessage) + + public Connection(SocketIOSocket socket, Action onMessage, Action onClose) { Socket = socket; + DeleteConnection = onClose; Socket.On(SocketIOEvent.DISCONNECT, OnDisconnect); Socket.On(SocketIOEvent.ERROR, (JToken[] Data) => OnError(Data)); Socket.On("message", (Action) onMessage); @@ -62,8 +65,9 @@ namespace PolyChat.Models private void OnDisconnect() { Debug.WriteLine("--- Disconnected! ---"); + Debug.WriteLine($"--- Deleting Connection with IP: {IP}---"); + DeleteConnection(IP, IsConnected()); Connected = false; - Close(); } private void OnError(JToken[] data) { From 8dab2af62771496a6ed057e88212f026f7739409 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Wed, 22 Sep 2021 16:21:40 +0200 Subject: [PATCH 10/82] small fix for correct ip display --- PolyChat/Controller.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 835a540..3180421 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -59,7 +59,7 @@ namespace PolyChat socket.On("initial", async (JToken[] data) => { Debug.WriteLine("--- initial packet received ---"); - string ForeignIp = data.ToString(); + string ForeignIp = data[0].ToString(); //Todo deserialize inital packet and extract ip address Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data))); UIController.OnIncomingConnection(ForeignIp); From 52422072a6ba86cd7d0df9b6e06f47c0aa846af8 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 08:41:22 +0200 Subject: [PATCH 11/82] retry working, radiobuttons now triggering on every click, deleteChat Button working --- PolyChat/MainPage.xaml | 2 +- PolyChat/MainPage.xaml.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/PolyChat/MainPage.xaml b/PolyChat/MainPage.xaml index ebdfd13..e2d66c4 100644 --- a/PolyChat/MainPage.xaml +++ b/PolyChat/MainPage.xaml @@ -53,7 +53,7 @@ - + diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 0dc0f43..b422b0a 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -114,7 +114,7 @@ namespace PolyChat /// ChatMessage public void OnIncomingMessage(string origin, string json) { - ChatPartner sendingPartner = Partners.First(p => p.Code == origin); + ChatPartner sendingPartner = Partners.FirstOrDefault(p => p.Code == origin); sendingPartner.AddMessage(new ChatMessage(origin, json)); } @@ -130,7 +130,7 @@ namespace PolyChat { await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { - Partners.Remove(Partners.First(p => p.Code == code)); + Partners.Remove(Partners.FirstOrDefault(p => p.Code.Equals(code))); selectedPartner = null; updateNoChatsPlaceholder(); updateNoChatSelected(); @@ -139,7 +139,7 @@ namespace PolyChat public void OnChatPartnerSelected(object sender, RoutedEventArgs e) { string code = ((RadioButton)sender).Tag.ToString(); - selectedPartner = Partners.First(p => p.Code == code); + selectedPartner = Partners.FirstOrDefault(p => p.Code == code); listViewMessages.ItemsSource = selectedPartner.Messages; selectedPartnerName.Text = selectedPartner.Name; updateNoChatSelected(); From 44ef2f4cd2e15b8035b0b3c95b056553fb527647 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 08:59:22 +0200 Subject: [PATCH 12/82] ConnectionFailedDialog -> Dialog for Success and Error Messages with heading, message and buttons/actions --- PolyChat/Controller.cs | 14 ++-- PolyChat/MainPage.xaml.cs | 38 ++++++---- PolyChat/Models/DialogButton.cs | 20 ++++++ PolyChat/PolyChat.csproj | 7 +- PolyChat/Views/ConnectionFailedDialog.xaml.cs | 26 ------- ...onnectionFailedDialog.xaml => Dialog.xaml} | 13 ++-- PolyChat/Views/Dialog.xaml.cs | 70 +++++++++++++++++++ 7 files changed, 132 insertions(+), 56 deletions(-) create mode 100644 PolyChat/Models/DialogButton.cs delete mode 100644 PolyChat/Views/ConnectionFailedDialog.xaml.cs rename PolyChat/Views/{ConnectionFailedDialog.xaml => Dialog.xaml} (59%) create mode 100644 PolyChat/Views/Dialog.xaml.cs diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index acb98a1..b272f6a 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -10,6 +10,10 @@ using Newtonsoft.Json; namespace PolyChat { + + // 10.1.211.26 Marc + // 10.1.218.90 Felix + // 10.4.141.77 Pat class Controller { // Constants @@ -30,10 +34,6 @@ namespace PolyChat UIController = uiController; OwnIP = getIP(); Serve(); - - //Connect("10.1.211.26"); // Marc - //Connect("10.1.218.90"); // Felix - //Connect("10.4.141.77"); // Pat } public void Connect(string ip) @@ -45,9 +45,7 @@ namespace PolyChat private void Serve() { Debug.WriteLine("--- Controller.Serve ---"); - SocketIOServer server = new SocketIOServer(new SocketIOServerOption( - PORT - )); + SocketIOServer server = new SocketIOServer(new SocketIOServerOption(PORT)); server.Start(); Debug.WriteLine("Port " + server.Option.Port); Debug.WriteLine("Path " + server.Option.Path); @@ -97,7 +95,7 @@ namespace PolyChat Connections.Remove(IP); UIController.OnChatPartnerDeleted(IP); if(!wasConnected) - UIController.ShowConnectionError(IP, $"Connection to {IP} failed..."); + UIController.ShowConnectionError("Connection close", IP, $"Connection to {IP} failed..."); } public string getIP() diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 0dc0f43..1eccd36 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -36,21 +36,31 @@ namespace PolyChat updateSendButtonEnabled(); } - public async void ShowConnectionError(string code, string message) + public async void ShowConnectionError(string code, string heading, string message) { - await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => + await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { - ConnectionFailedDialog dialog = new ConnectionFailedDialog(message); - var result = await dialog.ShowAsync(); - if (result == ContentDialogResult.Primary) - { - Controller.Connect(code); - Partners.Add(new ChatPartner( - "Connecting...", - code - )); - updateNoChatsPlaceholder(); - } + Dialog dialog = new Dialog( + Dialog.TYPE_ERROR, + heading, + message, + new DialogButton( + "Retry", + () => + { + Controller.Connect(code); + Partners.Add(new ChatPartner( + "Connecting...", + code + )); + updateNoChatsPlaceholder(); + } + ), + new DialogButton( + "Ignore", + () => { /* do nothing */ } + ) + ); }); } @@ -58,7 +68,7 @@ namespace PolyChat public void OnSendMessage(object sender = null, RoutedEventArgs e = null) { - selectedPartner.AddMessage(new ChatMessage(username, "message" , inputSend.Text)); + selectedPartner.AddMessage(new ChatMessage(username, "message", inputSend.Text)); Controller.SendMessage(selectedPartner.Code, "message", inputSend.Text); // clear input inputSend.Text = ""; diff --git a/PolyChat/Models/DialogButton.cs b/PolyChat/Models/DialogButton.cs new file mode 100644 index 0000000..959e5c2 --- /dev/null +++ b/PolyChat/Models/DialogButton.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PolyChat.Models +{ + public class DialogButton + { + public string Text; + public Action Action; + + public DialogButton(string text, Action action) + { + Text = text; + Action = action; + } + } +} diff --git a/PolyChat/PolyChat.csproj b/PolyChat/PolyChat.csproj index 3f479cf..418a809 100644 --- a/PolyChat/PolyChat.csproj +++ b/PolyChat/PolyChat.csproj @@ -119,6 +119,7 @@ App.xaml + @@ -130,8 +131,8 @@ - - ConnectionFailedDialog.xaml + + Dialog.xaml EditUsernameDialog.xaml @@ -164,7 +165,7 @@ MSBuild:Compile Designer - + MSBuild:Compile Designer diff --git a/PolyChat/Views/ConnectionFailedDialog.xaml.cs b/PolyChat/Views/ConnectionFailedDialog.xaml.cs deleted file mode 100644 index 1c09a8f..0000000 --- a/PolyChat/Views/ConnectionFailedDialog.xaml.cs +++ /dev/null @@ -1,26 +0,0 @@ -using PolyChat.Models; -using PolyChat.Util; -using System; -using System.Collections.ObjectModel; -using System.Linq; -using Windows.UI.Popups; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Media; - -// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 - -namespace PolyChat.Views -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class ConnectionFailedDialog : ContentDialog - { - public ConnectionFailedDialog(string message) - { - this.InitializeComponent(); - textError.Text = message; - } - } -} diff --git a/PolyChat/Views/ConnectionFailedDialog.xaml b/PolyChat/Views/Dialog.xaml similarity index 59% rename from PolyChat/Views/ConnectionFailedDialog.xaml rename to PolyChat/Views/Dialog.xaml index fa8916e..bc60eb8 100644 --- a/PolyChat/Views/ConnectionFailedDialog.xaml +++ b/PolyChat/Views/Dialog.xaml @@ -1,17 +1,20 @@ - - + + diff --git a/PolyChat/Views/Dialog.xaml.cs b/PolyChat/Views/Dialog.xaml.cs new file mode 100644 index 0000000..f342693 --- /dev/null +++ b/PolyChat/Views/Dialog.xaml.cs @@ -0,0 +1,70 @@ +using PolyChat.Models; +using PolyChat.Util; +using System; +using System.Collections.ObjectModel; +using System.Linq; +using Windows.Foundation; +using Windows.UI.Core; +using Windows.UI.Popups; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 + +namespace PolyChat.Views +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class Dialog : ContentDialog + { + public const string TYPE_ERROR = "error"; + public const string TYPE_SUCCESS = "success"; + private Action Primary; + private Action Secondary; + public Dialog(string type, string header, string message, DialogButton primary, DialogButton secondary) + { + this.InitializeComponent(); + Title = header; + setType(type, message); + PrimaryButtonText = primary.Text; + SecondaryButtonText = secondary.Text; + // TODO: use event handlers and asign actions here + Primary = primary.Action; + Secondary = secondary.Action; + // show + ShowDialogAsync(); + } + + private void setType(string type, string message) + { + switch (type) + { + case TYPE_ERROR: + textError.Text = message; + textError.Visibility = Visibility.Visible; + break; + case TYPE_SUCCESS: + textSuccess.Text = message; + textSuccess.Visibility = Visibility.Visible; + break; + } + } + + private async void ShowDialogAsync() + { + await ShowAsync(); + } + + private void OnPrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + Primary(); + } + + private void OnSecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + Secondary(); + } + } +} From 92939c82306dd835f26c62ad682a708fb49ef859 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 09:12:03 +0200 Subject: [PATCH 13/82] stop connection if we are already connected --- PolyChat/Controller.cs | 36 ++++++++++++++++++++++++++++++----- PolyChat/Models/Connection.cs | 5 +++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index b272f6a..87c9a9e 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -39,7 +39,14 @@ namespace PolyChat public void Connect(string ip) { Debug.WriteLine("--- Controller.Connect ---"); - Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(ip, Data), CloseChat)); + if (isInConnections(ip)) + { + Debug.WriteLine("---- We have an active connection to this client. ABORT! ----"); + CloseChatUI(ip); + //Todo show error! + } + else + Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(ip, Data), CloseChat)); } private void Serve() @@ -58,9 +65,16 @@ namespace PolyChat { Debug.WriteLine("--- initial packet received ---"); string ForeignIp = data[0].ToString(); - //Todo deserialize inital packet and extract ip address - Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data), CloseChat)); - UIController.OnIncomingConnection(ForeignIp); + if (isInConnections(ForeignIp)) + { + Debug.WriteLine("---- We have an active connection to this client. ABORT! ----");//Todo show error! + CloseChatUI(ForeignIp); + } + else + { + Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data), CloseChat)); + UIController.OnIncomingConnection(ForeignIp); + } }); }); } @@ -93,11 +107,23 @@ namespace PolyChat { Connections[IP].Close(); Connections.Remove(IP); + CloseChatUI(IP,wasConnected); + } + + private void CloseChatUI(string IP, bool wasConnected = true) + { UIController.OnChatPartnerDeleted(IP); - if(!wasConnected) + if (!wasConnected) UIController.ShowConnectionError("Connection close", IP, $"Connection to {IP} failed..."); } + private bool isInConnections(string IP) + { + if(Connections.ContainsKey(IP)) + return true; + return false; + } + public string getIP() { IPHostEntry ipEntry = Dns.GetHostEntry(Dns.GetHostName()); diff --git a/PolyChat/Models/Connection.cs b/PolyChat/Models/Connection.cs index 971e6af..b1d8600 100644 --- a/PolyChat/Models/Connection.cs +++ b/PolyChat/Models/Connection.cs @@ -92,5 +92,10 @@ namespace PolyChat.Models { return Connected; } + + public string getIP() + { + return IP; + } } } From cd26ac11c389da2bd711b967ccfddcb32cdfc8e4 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 09:41:56 +0200 Subject: [PATCH 14/82] allow deletion of chats which are not started by me --- PolyChat/Controller.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 87c9a9e..56c1460 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -105,8 +105,12 @@ namespace PolyChat public void CloseChat(string IP, bool wasConnected = true) { - Connections[IP].Close(); - Connections.Remove(IP); + Debug.WriteLine($"Deleting connection with IP:{IP}"); + if (IP != null && Connections.ContainsKey(IP)) + { + Connections[IP].Close(); + Connections.Remove(IP); + } CloseChatUI(IP,wasConnected); } From c22987d86f3eb697a7bd78d027bd2f2b9288c393 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 09:52:54 +0200 Subject: [PATCH 15/82] fix ip in intial packet (send own ip, not foreign) --- PolyChat/Controller.cs | 3 ++- PolyChat/Models/Connection.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 56c1460..9a187b3 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -65,6 +65,7 @@ namespace PolyChat { Debug.WriteLine("--- initial packet received ---"); string ForeignIp = data[0].ToString(); + Debug.WriteLine($"--- this ip was in the inital packet: {ForeignIp} ---"); if (isInConnections(ForeignIp)) { Debug.WriteLine("---- We have an active connection to this client. ABORT! ----");//Todo show error! @@ -128,7 +129,7 @@ namespace PolyChat return false; } - public string getIP() + public static string getIP() { IPHostEntry ipEntry = Dns.GetHostEntry(Dns.GetHostName()); IPAddress[] addrList = ipEntry.AddressList; diff --git a/PolyChat/Models/Connection.cs b/PolyChat/Models/Connection.cs index b1d8600..9c985b6 100644 --- a/PolyChat/Models/Connection.cs +++ b/PolyChat/Models/Connection.cs @@ -58,7 +58,7 @@ namespace PolyChat.Models private void OnConnect() { Debug.WriteLine("--- Sending initial packet to server ---"); - Client.Emit("initial", IP); + Client.Emit("initial", Controller.getIP()); Debug.WriteLine("--- Connection successfull ---"); Connected = true; } From 69d5327c75d5a0120fb7b18d8e116264731000db Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 10:21:55 +0200 Subject: [PATCH 16/82] Added OnIncomingMessages (for loading messages from json array) --- PolyChat/Controller.cs | 7 +++---- PolyChat/MainPage.xaml.cs | 38 +++++++++++++++++++++++++++------- PolyChat/Models/ChatMessage.cs | 30 ++++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 12 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index b272f6a..73ecb63 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -82,8 +82,7 @@ namespace PolyChat Debug.WriteLine("--- Controller.OnMessage ---"); if (data != null && data.Length > 0 && data[0] != null) { - Debug.WriteLine("Message: " + data[0]); - Debug.WriteLine($"RAW: {data[0].ToString()}"); + Debug.WriteLine("RAW: " + data[0]); UIController.OnIncomingMessage(ip, data[0].ToString()); } else Debug.WriteLine("Undefined: " + data); @@ -94,8 +93,8 @@ namespace PolyChat Connections[IP].Close(); Connections.Remove(IP); UIController.OnChatPartnerDeleted(IP); - if(!wasConnected) - UIController.ShowConnectionError("Connection close", IP, $"Connection to {IP} failed..."); + string heading = wasConnected ? "Connection Closed" : "Connection Failed"; + UIController.ShowConnectionError(IP, heading, $"Connecting to {IP} failed..."); } public string getIP() diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 08b3b2e..cfa30b5 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -1,8 +1,10 @@ -using PolyChat.Models; +using Newtonsoft.Json.Linq; +using PolyChat.Models; using PolyChat.Util; using PolyChat.Views; using System; using System.Collections.ObjectModel; +using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using Windows.UI.Core; @@ -36,7 +38,7 @@ namespace PolyChat updateSendButtonEnabled(); } - public async void ShowConnectionError(string code, string heading, string message) + public async void ShowConnectionError(string param, string heading, string message) { await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { @@ -48,10 +50,10 @@ namespace PolyChat "Retry", () => { - Controller.Connect(code); + Controller.Connect(param); Partners.Add(new ChatPartner( "Connecting...", - code + param )); updateNoChatsPlaceholder(); } @@ -122,10 +124,32 @@ namespace PolyChat /// Adds an message to the UI, based on .sender if known /// /// ChatMessage - public void OnIncomingMessage(string origin, string json) + public async void OnIncomingMessage(string origin, string json) { - ChatPartner sendingPartner = Partners.FirstOrDefault(p => p.Code == origin); - sendingPartner.AddMessage(new ChatMessage(origin, json)); + await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + ChatPartner sendingPartner = Partners.FirstOrDefault(p => p.Code == origin); + sendingPartner.AddMessage(new ChatMessage(origin, json)); + }); + } + public async void OnIncomingMessages(string origin, string json) + { + await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + ChatPartner sendingPartner = Partners.FirstOrDefault(p => p.Code == origin); + JArray arr = JArray.Parse(json); + foreach (JObject item in arr) + { + sendingPartner.AddMessage( + new ChatMessage( + origin, + item["type"].ToString(), + item["content"].ToString()//, + //DateTime.Parse(item["timestamp"].ToString()) + ) + ); + } + }); } private void OnDeleteChat(object sender = null, RoutedEventArgs e = null) diff --git a/PolyChat/Models/ChatMessage.cs b/PolyChat/Models/ChatMessage.cs index 30b3ea2..9fd22c2 100644 --- a/PolyChat/Models/ChatMessage.cs +++ b/PolyChat/Models/ChatMessage.cs @@ -11,7 +11,13 @@ namespace PolyChat.Models public string Content; public DateTime TimeStamp; public readonly bool Foreign; - // + + /// + /// Create own Message (directly sent) + /// + /// My IP + /// Message Type + /// Message Content (not JSON) public ChatMessage(string origin, string type, string content) { Origin = origin; @@ -23,6 +29,28 @@ namespace PolyChat.Models Debug.WriteLine("Created Message: " + ToString()); } + /// + /// Create Message loaded with timestamp + /// + /// Origin IP + /// Message Type + /// Message Content (not JSON) + /// Message Content (not JSON) + public ChatMessage(string origin, string type, string content, DateTime timeStamp, bool foreign = false) + { + Origin = origin; + TimeStamp = timeStamp; + Type = type; + Content = content; + Foreign = foreign; + Debug.WriteLine("Created Loaded Message: " + ToString()); + } + + /// + /// Create foreign Message (directly incoming) + /// + /// Foreign IP + /// Message Content as JSON with type and content public ChatMessage(string origin, string json) { Origin = origin; From 89059491c9f38786dda1fc65c50b55ac5bbc6ed8 Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 10:50:40 +0200 Subject: [PATCH 17/82] Saving and loading but still has error(Pls fix) --- PolyChat/Controller.cs | 99 +++++++++++++++++++++++++++++++++++++-- PolyChat/MainPage.xaml.cs | 1 + 2 files changed, 96 insertions(+), 4 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 8b8b1eb..5a77266 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -5,8 +5,10 @@ using System.Net; using SocketIOSharp.Server; using SocketIOSharp.Server.Client; using PolyChat.Models; +using System.IO; +using System.Threading; +using System; using System.Text.Json; -using Newtonsoft.Json; namespace PolyChat { @@ -33,6 +35,8 @@ namespace PolyChat { UIController = uiController; OwnIP = getIP(); + loadChats(); + //SaveChats("10", "{das ist ein test}"); Serve(); } @@ -86,10 +90,12 @@ namespace PolyChat Debug.WriteLine($"{type} -> {ip} content: {content}"); JObject json = new JObject( new JProperty("type", type), - new JProperty("content", content) + new JProperty("content", content), + new JProperty("timestamp", DateTime.Now.ToString()) ); Debug.WriteLine($"json: {json.ToString()}"); Connections[ip].SendMessage(json.ToString()); + SaveChats(ip, json.ToString()); } private void OnMessage(string ip, JToken[] data) @@ -99,6 +105,7 @@ namespace PolyChat { Debug.WriteLine("RAW: " + data[0]); UIController.OnIncomingMessage(ip, data[0].ToString()); + SaveChats(ip, data[0].ToString()); } else Debug.WriteLine("Undefined: " + data); } @@ -111,7 +118,7 @@ namespace PolyChat Connections[IP].Close(); Connections.Remove(IP); } - CloseChatUI(IP,wasConnected); + CloseChatUI(IP, wasConnected); } private void CloseChatUI(string IP, bool wasConnected = true) @@ -123,7 +130,7 @@ namespace PolyChat private bool isInConnections(string IP) { - if(Connections.ContainsKey(IP)) + if (Connections.ContainsKey(IP)) return true; return false; } @@ -141,5 +148,89 @@ namespace PolyChat } return null; } + + /// + /// sends chatlogs as json array to uiController wit corrosponding ip + /// + /// + public void loadChats() + { + //load dir and create if non existant + if (Directory.Exists("U:\\PolyChat\\Saves")) + { + Debug.WriteLine("--Path exists.--"); + } + else + { + Directory.CreateDirectory("U:\\PolyChat\\Saves"); + Debug.WriteLine("--Path Created--."); + } + + //go through all files and send ip and json array to ui + String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); + if (filepaths.Length > 0) + { + foreach (String path in filepaths) + { + Debug.WriteLine("---Loading Saves---"); + Debug.WriteLine("--" + path + "--"); + String jsonArr = File.ReadAllText(path); + String ip = Path.GetFileName(path); + ip = ip.Substring(0, ip.Length - 4); + Debug.WriteLine("-" + ip + "-"); + Debug.WriteLine(jsonArr); + UIController.OnIncomingMessages(ip, jsonArr); + } + } + + } + + /// + /// Saves incoming chat message to + /// + /// + /// + public void SaveChats(String ip, String json) + { + //Vielleicht noch so machen dass die mit gleicher ip nacheinander gemacht + //werden damit es ncith zu überschreibungen kommt vielleicth auch ganz oben oder am ende ne + //writing flag setzen oder auch in der datei selbst ne flag setzen + //also save fils from myself + new Thread(() => + { + if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) + { + Debug.WriteLine("--File allready exists--"); + //check for integraty of file + string output = File.ReadAllText($"U:\\PolyChat\\Saves\\{ip}.txt"); + Debug.WriteLine($"---{output}---"); + if (output.Substring(0, 1).Equals("[") && output.Substring(output.Length - 1, 1).Equals("]")) + { + Debug.WriteLine("--adding new chatmessage--"); + //structure intact + //save new chat + String saved = output.Substring(0, output.Length - 1); + output = saved + ", " + json + " ]"; + File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", output); + } + else + { + Debug.WriteLine("--Structure not intact--"); + Debug.WriteLine("--redoing file--"); + //structure not intact + //redo file + File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", $"[ {json} ]"); + } + } + else + { + Debug.WriteLine("--Creating new File--"); + //setup file + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", $"[ {json} ]"); + } + }).Start(); + } } } diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index cfa30b5..a2f244a 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -152,6 +152,7 @@ namespace PolyChat }); } + private void OnDeleteChat(object sender = null, RoutedEventArgs e = null) { Controller.CloseChat(selectedPartner.Code); From 871ed22956555d5bec1e8e26bd82c97f7e2e4470 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 10:56:10 +0200 Subject: [PATCH 18/82] Added BoardcastMessage, Username Changed event via Broadcast, remove json parsing from ChatMessage --- PolyChat/Controller.cs | 13 +++++++++++-- PolyChat/MainPage.xaml.cs | 20 ++++++++++++++++---- PolyChat/Models/ChatMessage.cs | 20 +------------------- PolyChat/Models/ChatPartner.cs | 5 +++++ 4 files changed, 33 insertions(+), 25 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 8b8b1eb..0c9b683 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -80,6 +80,15 @@ namespace PolyChat }); } + public void SendBroadcastMessage(string type, string content) + { + Debug.WriteLine("--- Controller.Broadcast ---"); + foreach (KeyValuePair entry in Connections) + { + SendMessage(entry.Key, type, content); + } + } + public void SendMessage(string ip, string type, string content) { Debug.WriteLine("--- Controller.SendMessage ---"); @@ -111,7 +120,7 @@ namespace PolyChat Connections[IP].Close(); Connections.Remove(IP); } - CloseChatUI(IP,wasConnected); + CloseChatUI(IP, wasConnected); } private void CloseChatUI(string IP, bool wasConnected = true) @@ -123,7 +132,7 @@ namespace PolyChat private bool isInConnections(string IP) { - if(Connections.ContainsKey(IP)) + if (Connections.ContainsKey(IP)) return true; return false; } diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index cfa30b5..9f1fded 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -6,6 +6,7 @@ using System; using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; +using System.Text.Json; using System.Threading.Tasks; using Windows.UI.Core; using Windows.UI.Xaml; @@ -99,8 +100,8 @@ namespace PolyChat if (result == ContentDialogResult.Primary) { username = dialog.getValue(); - if (username.Length == 0) textUsername.Text = "Unknown"; - else textUsername.Text = username; + textUsername.Text = username; + Controller.SendBroadcastMessage("username", username); } updateNoUsernamePlaceholder(); } @@ -128,8 +129,19 @@ namespace PolyChat { await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { + var doc = JsonDocument.Parse(json).RootElement; + string type = doc.GetProperty("type").GetString(); + string content = doc.GetProperty("content").GetString(); ChatPartner sendingPartner = Partners.FirstOrDefault(p => p.Code == origin); - sendingPartner.AddMessage(new ChatMessage(origin, json)); + switch (type) + { + case "username": + sendingPartner.SetName(Name); + break; + default: + sendingPartner.AddMessage(new ChatMessage(origin, type, content)); + break; + } }); } public async void OnIncomingMessages(string origin, string json) @@ -145,7 +157,7 @@ namespace PolyChat origin, item["type"].ToString(), item["content"].ToString()//, - //DateTime.Parse(item["timestamp"].ToString()) + //DateTime.Parse(item["timestamp"].ToString()) ) ); } diff --git a/PolyChat/Models/ChatMessage.cs b/PolyChat/Models/ChatMessage.cs index 9fd22c2..5757702 100644 --- a/PolyChat/Models/ChatMessage.cs +++ b/PolyChat/Models/ChatMessage.cs @@ -24,7 +24,7 @@ namespace PolyChat.Models TimeStamp = DateTime.Now; Type = type; Content = content; - // no json = my messages + // TODO Foreign = false; Debug.WriteLine("Created Message: " + ToString()); } @@ -46,24 +46,6 @@ namespace PolyChat.Models Debug.WriteLine("Created Loaded Message: " + ToString()); } - /// - /// Create foreign Message (directly incoming) - /// - /// Foreign IP - /// Message Content as JSON with type and content - public ChatMessage(string origin, string json) - { - Origin = origin; - // parse and save to object - var obj = JsonDocument.Parse(json).RootElement; - Type = obj.GetProperty("type").GetString(); - Content = obj.GetProperty("content").GetString(); - TimeStamp = DateTime.Now; - // json = foreign - Foreign = true; - Debug.WriteLine("Created Message: " + ToString()); - } - override public string ToString() { diff --git a/PolyChat/Models/ChatPartner.cs b/PolyChat/Models/ChatPartner.cs index 020433d..118b7af 100644 --- a/PolyChat/Models/ChatPartner.cs +++ b/PolyChat/Models/ChatPartner.cs @@ -24,5 +24,10 @@ namespace PolyChat.Models { Messages.Add(message); } + + public void SetName(string name) + { + Name = name; + } } } \ No newline at end of file From 766a8171f00fa053078650cce6b04fc3c6b9567a Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 11:09:43 +0200 Subject: [PATCH 19/82] fix connection establishment when loading files --- PolyChat/Controller.cs | 3 +++ PolyChat/MainPage.xaml.cs | 5 +++++ PolyChat/Models/ChatMessage.cs | 7 ++++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 61793fb..e390a9e 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -164,6 +164,8 @@ namespace PolyChat /// public void loadChats() { + + //load dir and create if non existant if (Directory.Exists("U:\\PolyChat\\Saves")) { @@ -188,6 +190,7 @@ namespace PolyChat ip = ip.Substring(0, ip.Length - 4); Debug.WriteLine("-" + ip + "-"); Debug.WriteLine(jsonArr); + Connect(ip); UIController.OnIncomingMessages(ip, jsonArr); } } diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 7cc022e..245e679 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -156,8 +156,13 @@ namespace PolyChat new ChatMessage( origin, item["type"].ToString(), +<<<<<<< Updated upstream item["content"].ToString()//, //DateTime.Parse(item["timestamp"].ToString()) +======= + item["content"].ToString(), + DateTime.Parse(item["timestamp"].ToString()) +>>>>>>> Stashed changes ) ); } diff --git a/PolyChat/Models/ChatMessage.cs b/PolyChat/Models/ChatMessage.cs index 5757702..55c593a 100644 --- a/PolyChat/Models/ChatMessage.cs +++ b/PolyChat/Models/ChatMessage.cs @@ -12,6 +12,11 @@ namespace PolyChat.Models public DateTime TimeStamp; public readonly bool Foreign; + + public ChatMessage() + { + + } /// /// Create own Message (directly sent) /// @@ -43,7 +48,7 @@ namespace PolyChat.Models Type = type; Content = content; Foreign = foreign; - Debug.WriteLine("Created Loaded Message: " + ToString()); + Debug.WriteLine("Created Loaded Message: " + ToString()); } override From 51ada88313d0c604fc94fa556132ff87acf2e21d Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 11:09:58 +0200 Subject: [PATCH 20/82] merge --- PolyChat/MainPage.xaml.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 245e679..b91a0bb 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -156,13 +156,8 @@ namespace PolyChat new ChatMessage( origin, item["type"].ToString(), -<<<<<<< Updated upstream - item["content"].ToString()//, - //DateTime.Parse(item["timestamp"].ToString()) -======= item["content"].ToString(), DateTime.Parse(item["timestamp"].ToString()) ->>>>>>> Stashed changes ) ); } From 59630cc137d71657073daf94dd330f1b0929826c Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 11:29:54 +0200 Subject: [PATCH 21/82] quick --- PolyChat/Controller.cs | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index e390a9e..1f3315b 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -9,6 +9,8 @@ using System.IO; using System.Threading; using System; using System.Text.Json; +using System.Text; +using System.Security.Cryptography; namespace PolyChat { @@ -50,7 +52,10 @@ namespace PolyChat //Todo show error! } else + { Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(ip, Data), CloseChat)); + } + } private void Serve() @@ -164,8 +169,6 @@ namespace PolyChat /// public void loadChats() { - - //load dir and create if non existant if (Directory.Exists("U:\\PolyChat\\Saves")) { @@ -181,16 +184,17 @@ namespace PolyChat String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); if (filepaths.Length > 0) { + Debug.WriteLine("---Loading Saves"); foreach (String path in filepaths) { - Debug.WriteLine("---Loading Saves---"); - Debug.WriteLine("--" + path + "--"); + Debug.WriteLine($"--{path}"); String jsonArr = File.ReadAllText(path); String ip = Path.GetFileName(path); ip = ip.Substring(0, ip.Length - 4); - Debug.WriteLine("-" + ip + "-"); + Debug.WriteLine($"-{ip}"); Debug.WriteLine(jsonArr); Connect(ip); + UIController.OnIncomingConnection(ip); UIController.OnIncomingMessages(ip, jsonArr); } } @@ -207,7 +211,6 @@ namespace PolyChat //Vielleicht noch so machen dass die mit gleicher ip nacheinander gemacht //werden damit es ncith zu überschreibungen kommt vielleicth auch ganz oben oder am ende ne //writing flag setzen oder auch in der datei selbst ne flag setzen - //also save fils from myself new Thread(() => { if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) @@ -244,5 +247,18 @@ namespace PolyChat } }).Start(); } + + private void encode(string json) + { + byte[] plaintext = Encoding.UTF8.GetBytes(json); + byte[] entropy = new byte[20]; + using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider()) + { + rng.GetBytes(entropy); + } + + /*byte[] ciphertext = ProtectedData.Protect(plaintext, entropy, + DataProtectionScope.CurrentUser);*/ + } } } From 8121a8dbfd0b213aca0bc6b2146ee60b3bea2d6f Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 11:30:09 +0200 Subject: [PATCH 22/82] Removed timeStamp from SendMessage, added param Timestamp to savechat() --- PolyChat/Controller.cs | 17 ++++++++++------- PolyChat/MainPage.xaml.cs | 8 ++++---- PolyChat/Models/ChatMessage.cs | 6 +++--- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 61793fb..0a7c9fe 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -99,12 +99,13 @@ namespace PolyChat Debug.WriteLine($"{type} -> {ip} content: {content}"); JObject json = new JObject( new JProperty("type", type), - new JProperty("content", content), - new JProperty("timestamp", DateTime.Now.ToString()) + new JProperty("content", content) ); Debug.WriteLine($"json: {json.ToString()}"); + // send as json Connections[ip].SendMessage(json.ToString()); - SaveChats(ip, json.ToString()); + // save to logs + SaveChats(ip, json.ToString(), DateTime.Now); } private void OnMessage(string ip, JToken[] data) @@ -112,9 +113,10 @@ namespace PolyChat Debug.WriteLine("--- Controller.OnMessage ---"); if (data != null && data.Length > 0 && data[0] != null) { + DateTime now = DateTime.Now; Debug.WriteLine("RAW: " + data[0]); - UIController.OnIncomingMessage(ip, data[0].ToString()); - SaveChats(ip, data[0].ToString()); + UIController.OnIncomingMessage(ip, data[0].ToString(), now); + SaveChats(ip, data[0].ToString(), now); } else Debug.WriteLine("Undefined: " + data); } @@ -199,10 +201,10 @@ namespace PolyChat /// /// /// - public void SaveChats(String ip, String json) + public void SaveChats(string ip, string json, DateTime timeStamp) { //Vielleicht noch so machen dass die mit gleicher ip nacheinander gemacht - //werden damit es ncith zu überschreibungen kommt vielleicth auch ganz oben oder am ende ne + //werden damit es nicht zu überschreibungen kommt vielleicth auch ganz oben oder am ende ne //writing flag setzen oder auch in der datei selbst ne flag setzen //also save fils from myself new Thread(() => @@ -217,6 +219,7 @@ namespace PolyChat { Debug.WriteLine("--adding new chatmessage--"); //structure intact + JObject obj = JObject.Parse(json); //save new chat String saved = output.Substring(0, output.Length - 1); output = saved + ", " + json + " ]"; diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 7cc022e..c3c3b84 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -125,7 +125,7 @@ namespace PolyChat /// Adds an message to the UI, based on .sender if known /// /// ChatMessage - public async void OnIncomingMessage(string origin, string json) + public async void OnIncomingMessage(string origin, string json, DateTime timeStamp) { await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { @@ -139,7 +139,7 @@ namespace PolyChat sendingPartner.SetName(Name); break; default: - sendingPartner.AddMessage(new ChatMessage(origin, type, content)); + sendingPartner.AddMessage(new ChatMessage(origin, type, content, timeStamp)); break; } }); @@ -156,8 +156,8 @@ namespace PolyChat new ChatMessage( origin, item["type"].ToString(), - item["content"].ToString()//, - //DateTime.Parse(item["timestamp"].ToString()) + item["content"].ToString(), + DateTime.Parse(item["timestamp"].ToString()) ) ); } diff --git a/PolyChat/Models/ChatMessage.cs b/PolyChat/Models/ChatMessage.cs index 5757702..32e6f49 100644 --- a/PolyChat/Models/ChatMessage.cs +++ b/PolyChat/Models/ChatMessage.cs @@ -33,9 +33,9 @@ namespace PolyChat.Models /// Create Message loaded with timestamp /// /// Origin IP - /// Message Type - /// Message Content (not JSON) - /// Message Content (not JSON) + /// Message Type, usually "message" + /// Message Content, usually plain text + /// Parsed DateTime public ChatMessage(string origin, string type, string content, DateTime timeStamp, bool foreign = false) { Origin = origin; From 512648d90799a06d4dbc6232f192e9be74539ae8 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 12:40:57 +0200 Subject: [PATCH 23/82] tested build and deploy via apppackage, fixed typo in IP.cs --- PolyChat/MainPage.xaml.cs | 2 +- PolyChat/Package.appxmanifest | 4 ++-- PolyChat/PolyChat.csproj | 13 ++++++++++++- PolyChat/Util/IP.cs | 2 +- PolyChat/Views/NewChatDialog.xaml.cs | 2 +- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index c3c3b84..30903ce 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -83,7 +83,7 @@ namespace PolyChat var result = await dialog.ShowAsync(); if (result == ContentDialogResult.Primary) { - string ip = IP.GetIPfromCode(dialog.getValue()); + string ip = IP.GetIPFromCode(dialog.getValue()); Controller.Connect(ip); Partners.Add(new ChatPartner( "Connecting...", diff --git a/PolyChat/Package.appxmanifest b/PolyChat/Package.appxmanifest index 06228f6..30ac9c8 100644 --- a/PolyChat/Package.appxmanifest +++ b/PolyChat/Package.appxmanifest @@ -9,8 +9,8 @@ + Publisher="CN=PatrickMarcFelix" + Version="0.1.0.0" /> diff --git a/PolyChat/PolyChat.csproj b/PolyChat/PolyChat.csproj index 418a809..12041a4 100644 --- a/PolyChat/PolyChat.csproj +++ b/PolyChat/PolyChat.csproj @@ -17,7 +17,15 @@ 512 {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} true - false + False + False + False + True + Always + x86 + 0 + 6B0D12FC4E83C6F6997C60706A04799ED44B7E56 + SHA256 true @@ -195,6 +203,9 @@ 5.0.2 + + + 14.0 diff --git a/PolyChat/Util/IP.cs b/PolyChat/Util/IP.cs index fcb6fd6..975dcd1 100644 --- a/PolyChat/Util/IP.cs +++ b/PolyChat/Util/IP.cs @@ -7,7 +7,7 @@ namespace PolyChat.Util { private const string REGEX_IP = @"^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$"; - public static string GetIPfromCode(string code) + public static string GetIPFromCode(string code) { return code; } diff --git a/PolyChat/Views/NewChatDialog.xaml.cs b/PolyChat/Views/NewChatDialog.xaml.cs index 600c62f..4c888ba 100644 --- a/PolyChat/Views/NewChatDialog.xaml.cs +++ b/PolyChat/Views/NewChatDialog.xaml.cs @@ -29,7 +29,7 @@ namespace PolyChat.Views private void OnKeyUp(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e) { - if (!IP.ValidateIP(IP.GetIPfromCode(input.Text))) + if (!IP.ValidateIP(IP.GetIPFromCode(input.Text))) { textSuccess.Visibility = Visibility.Collapsed; textError.Visibility = Visibility.Visible; From 924676bee691dbd283b4e613d820b74eb73861d6 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 12:53:57 +0200 Subject: [PATCH 24/82] Chats now only try to connect, if they are opened --- PolyChat/Controller.cs | 9 +++++---- PolyChat/MainPage.xaml.cs | 7 +++++-- PolyChat/Models/ChatPartner.cs | 11 ++++++++++- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index ec9ee05..989b9e2 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -146,9 +146,11 @@ namespace PolyChat private bool isInConnections(string IP) { - if (Connections.ContainsKey(IP)) - return true; - return false; + return Connections.ContainsKey(IP); + } + public bool IsConnected(string ip) + { + return Connections.ContainsKey(ip) && Connections[ip].IsConnected(); } public static string getIP() @@ -195,7 +197,6 @@ namespace PolyChat ip = ip.Substring(0, ip.Length - 4); Debug.WriteLine($"-{ip}"); Debug.WriteLine(jsonArr); - Connect(ip); UIController.OnIncomingConnection(ip); UIController.OnIncomingMessages(ip, jsonArr); } diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index c3c3b84..d39e27d 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Windows.UI.Core; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Data; // The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 @@ -136,6 +137,7 @@ namespace PolyChat switch (type) { case "username": + Debug.WriteLine($"! username change for {sendingPartner.Code} -> {content}"); sendingPartner.SetName(Name); break; default: @@ -156,8 +158,8 @@ namespace PolyChat new ChatMessage( origin, item["type"].ToString(), - item["content"].ToString(), - DateTime.Parse(item["timestamp"].ToString()) + item["content"].ToString()//, + //DateTime.Parse(item["timestamp"].ToString()) ) ); } @@ -190,6 +192,7 @@ namespace PolyChat listViewMessages.ItemsSource = selectedPartner.Messages; selectedPartnerName.Text = selectedPartner.Name; updateNoChatSelected(); + if (!Controller.IsConnected(code)) Controller.Connect(code); } private void OnKeyUp(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e) diff --git a/PolyChat/Models/ChatPartner.cs b/PolyChat/Models/ChatPartner.cs index 118b7af..32df3be 100644 --- a/PolyChat/Models/ChatPartner.cs +++ b/PolyChat/Models/ChatPartner.cs @@ -1,14 +1,17 @@ using SocketIOSharp.Server.Client; using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Runtime.CompilerServices; namespace PolyChat.Models { - public class ChatPartner + public class ChatPartner : INotifyPropertyChanged { public string Name; public string Code; public ObservableCollection Messages; private SocketIOSocket socketIOSocket; + public event PropertyChangedEventHandler PropertyChanged; public ChatPartner(string name, string code, ObservableCollection messages = null) { @@ -20,6 +23,11 @@ namespace PolyChat.Models public SocketIOSocket SocketIOSocket { get => socketIOSocket; set => socketIOSocket = value; } + private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + public void AddMessage(ChatMessage message) { Messages.Add(message); @@ -28,6 +36,7 @@ namespace PolyChat.Models public void SetName(string name) { Name = name; + NotifyPropertyChanged("Name"); } } } \ No newline at end of file From be4eada18a6a03662df279d595330cbe0c463c52 Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 12:55:31 +0200 Subject: [PATCH 25/82] stuff --- PolyChat/Controller.cs | 104 +++++++++++++++++++++++---------- PolyChat/MainPage.xaml.cs | 4 +- PolyChat/Models/ChatMessage.cs | 5 -- 3 files changed, 74 insertions(+), 39 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index ec9ee05..b7573d5 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -37,8 +37,6 @@ namespace PolyChat { UIController = uiController; OwnIP = getIP(); - loadChats(); - //SaveChats("10", "{das ist ein test}"); Serve(); } @@ -55,7 +53,7 @@ namespace PolyChat { Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(ip, Data), CloseChat)); } - + } private void Serve() @@ -74,6 +72,7 @@ namespace PolyChat { Debug.WriteLine("--- initial packet received ---"); string ForeignIp = data[0].ToString(); + Debug.WriteLine($"--- this ip was in the inital packet: {ForeignIp} ---"); if (isInConnections(ForeignIp)) { @@ -171,6 +170,7 @@ namespace PolyChat /// public void loadChats() { + //TODO: also load chatlogs when user tries to connect //load dir and create if non existant if (Directory.Exists("U:\\PolyChat\\Saves")) { @@ -200,9 +200,43 @@ namespace PolyChat UIController.OnIncomingMessages(ip, jsonArr); } } - } + /* + public void loadChat(String ip) + { + //TODO: also load chatlogs when user tries to connect + //load dir and create if non existant + if (Directory.Exists("U:\\PolyChat\\Saves")) + { + Debug.WriteLine("--Path exists.--"); + } + else + { + Directory.CreateDirectory("U:\\PolyChat\\Saves"); + Debug.WriteLine("--Path Created--."); + } + + //go through all files and send ip and json array to ui + String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); + if (filepaths.Length > 0) + { + Debug.WriteLine("---Loading Saves"); + foreach (String path in filepaths) + { + Debug.WriteLine($"--{path}"); + String jsonArr = File.ReadAllText(path); + String ip = Path.GetFileName(path); + ip = ip.Substring(0, ip.Length - 4); + Debug.WriteLine($"-{ip}"); + Debug.WriteLine(jsonArr); + Connect(ip); + UIController.OnIncomingConnection(ip); + UIController.OnIncomingMessages(ip, jsonArr); + } + } + } + */ /// /// Saves incoming chat message to /// @@ -215,43 +249,51 @@ namespace PolyChat //writing flag setzen oder auch in der datei selbst ne flag setzen new Thread(() => { - if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) + //breaking if namechange + JObject obj = JObject.Parse(json); + if (!obj["type"].ToString().Equals("username")) { - Debug.WriteLine("--File allready exists--"); - //check for integraty of file - string output = File.ReadAllText($"U:\\PolyChat\\Saves\\{ip}.txt"); - Debug.WriteLine($"---{output}---"); - if (output.Substring(0, 1).Equals("[") && output.Substring(output.Length - 1, 1).Equals("]")) + //adding timestamp + obj = JObject.Parse(json); + obj.Add(new JProperty("timestamp", timeStamp)); + json = obj.ToString(); + if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) { - Debug.WriteLine("--adding new chatmessage--"); - //structure intact - JObject obj = JObject.Parse(json); - //save new chat - String saved = output.Substring(0, output.Length - 1); - output = saved + ", " + json + " ]"; - File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); - File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", output); + Debug.WriteLine("--File allready exists--"); + //check for integraty of file + string output = File.ReadAllText($"U:\\PolyChat\\Saves\\{ip}.txt"); + Debug.WriteLine($"---{output}---"); + if (output.Substring(0, 1).Equals("[") && output.Substring(output.Length - 1, 1).Equals("]")) + { + Debug.WriteLine("--adding new chatmessage--"); + //structure intact + //save new chat + String saved = output.Substring(0, output.Length - 1); + output = saved + ", " + json + " ]"; + File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", output); + } + else + { + Debug.WriteLine("--Structure not intact--"); + Debug.WriteLine("--redoing file--"); + //structure not intact + //redo file + File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", $"[ {json} ]"); + } } else { - Debug.WriteLine("--Structure not intact--"); - Debug.WriteLine("--redoing file--"); - //structure not intact - //redo file - File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); + Debug.WriteLine("--Creating new File--"); + //setup file File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", $"[ {json} ]"); } } - else - { - Debug.WriteLine("--Creating new File--"); - //setup file - File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", $"[ {json} ]"); - } }).Start(); } - private void encode(string json) + private void encode(string json) { byte[] plaintext = Encoding.UTF8.GetBytes(json); byte[] entropy = new byte[20]; @@ -260,8 +302,6 @@ namespace PolyChat rng.GetBytes(entropy); } - /*byte[] ciphertext = ProtectedData.Protect(plaintext, entropy, - DataProtectionScope.CurrentUser);*/ } } } diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index c3c3b84..2f4788a 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -156,8 +156,8 @@ namespace PolyChat new ChatMessage( origin, item["type"].ToString(), - item["content"].ToString(), - DateTime.Parse(item["timestamp"].ToString()) + item["content"].ToString()//, + //DateTime.Parse(item["timestamp"].ToString()) ) ); } diff --git a/PolyChat/Models/ChatMessage.cs b/PolyChat/Models/ChatMessage.cs index 4056559..7428068 100644 --- a/PolyChat/Models/ChatMessage.cs +++ b/PolyChat/Models/ChatMessage.cs @@ -11,12 +11,7 @@ namespace PolyChat.Models public string Content; public DateTime TimeStamp; public readonly bool Foreign; - - public ChatMessage() - { - - } /// /// Create own Message (directly sent) /// From f07793bb93409030ea6576186f15b1869d39843f Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 13:29:04 +0200 Subject: [PATCH 26/82] only open dialog if there are no popups already open --- PolyChat/Controller.cs | 1 - PolyChat/MainPage.xaml.cs | 29 +++++++++++++++++++++++++---- PolyChat/PolyChat.csproj | 3 --- PolyChat/Views/Dialog.xaml.cs | 3 ++- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 5f114e6..4676a5b 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -8,7 +8,6 @@ using PolyChat.Models; using System.IO; using System.Threading; using System; -using System.Text.Json; using System.Text; using System.Security.Cryptography; diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 06a9db4..0c9442d 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -7,11 +7,11 @@ using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; using System.Text.Json; -using System.Threading.Tasks; +using Windows.Foundation; using Windows.UI.Core; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Media; // The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 @@ -81,7 +81,7 @@ namespace PolyChat public async void OnOpenNewChatDialog(object sender = null, RoutedEventArgs e = null) { NewChatDialog dialog = new NewChatDialog(); - var result = await dialog.ShowAsync(); + var result = await SafelyOpenDialog(dialog); if (result == ContentDialogResult.Primary) { string ip = IP.GetIPFromCode(dialog.getValue()); @@ -97,7 +97,7 @@ namespace PolyChat public async void OnOpenEditUsernameDialog(object sender = null, RoutedEventArgs e = null) { EditUsernameDialog dialog = new EditUsernameDialog(username); - var result = await dialog.ShowAsync(); + var result = await SafelyOpenDialog(dialog); if (result == ContentDialogResult.Primary) { username = dialog.getValue(); @@ -204,6 +204,27 @@ namespace PolyChat } } + public static IAsyncOperation SafelyOpenDialog(Dialog d) + { + if(VisualTreeHelper.GetOpenPopups(Window.Current).Count == 0) + return d.ShowAsync(); + return null; + } + + public static IAsyncOperation SafelyOpenDialog(NewChatDialog d) + { + if (VisualTreeHelper.GetOpenPopups(Window.Current).Count == 0) + return d.ShowAsync(); + return null; + } + + public static IAsyncOperation SafelyOpenDialog(EditUsernameDialog d) + { + if (VisualTreeHelper.GetOpenPopups(Window.Current).Count == 0) + return d.ShowAsync(); + return null; + } + // UPDATE FUNCTIONS FOR UI PLACEHOLDERS private void updateNoChatsPlaceholder() diff --git a/PolyChat/PolyChat.csproj b/PolyChat/PolyChat.csproj index 12041a4..42b0df9 100644 --- a/PolyChat/PolyChat.csproj +++ b/PolyChat/PolyChat.csproj @@ -203,9 +203,6 @@ 5.0.2 - - - 14.0 diff --git a/PolyChat/Views/Dialog.xaml.cs b/PolyChat/Views/Dialog.xaml.cs index f342693..331fb6f 100644 --- a/PolyChat/Views/Dialog.xaml.cs +++ b/PolyChat/Views/Dialog.xaml.cs @@ -3,6 +3,7 @@ using PolyChat.Util; using System; using System.Collections.ObjectModel; using System.Linq; +using System.Threading; using Windows.Foundation; using Windows.UI.Core; using Windows.UI.Popups; @@ -34,7 +35,7 @@ namespace PolyChat.Views Primary = primary.Action; Secondary = secondary.Action; // show - ShowDialogAsync(); + MainPage.SafelyOpenDialog(this); } private void setType(string type, string message) From 665d289d3a8fd86b8fd2cdaedd7956ecacfe6b1b Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 13:33:03 +0200 Subject: [PATCH 27/82] brgin of encryption --- PolyChat/Controller.cs | 52 ++++----------------- PolyChat/Models/FileManager.cs | 84 ++++++++++++++++++++++++++++++++++ PolyChat/PolyChat.csproj | 1 + 3 files changed, 95 insertions(+), 42 deletions(-) create mode 100644 PolyChat/Models/FileManager.cs diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 5f114e6..1d0c322 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -11,6 +11,9 @@ using System; using System.Text.Json; using System.Text; using System.Security.Cryptography; +using Windows.Security.Cryptography.Core; +using Windows.Security.Cryptography; +using Windows.Storage.Streams; namespace PolyChat { @@ -37,6 +40,8 @@ namespace PolyChat { UIController = uiController; OwnIP = getIP(); + loadChats(); + encode("test"); Serve(); } @@ -168,6 +173,8 @@ namespace PolyChat /// /// sends chatlogs as json array to uiController wit corrosponding ip + /// + /// in ui when chat is clicked connection gets established /// /// public void loadChats() @@ -203,41 +210,6 @@ namespace PolyChat } } - /* - public void loadChat(String ip) - { - //TODO: also load chatlogs when user tries to connect - //load dir and create if non existant - if (Directory.Exists("U:\\PolyChat\\Saves")) - { - Debug.WriteLine("--Path exists.--"); - } - else - { - Directory.CreateDirectory("U:\\PolyChat\\Saves"); - Debug.WriteLine("--Path Created--."); - } - - //go through all files and send ip and json array to ui - String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); - if (filepaths.Length > 0) - { - Debug.WriteLine("---Loading Saves"); - foreach (String path in filepaths) - { - Debug.WriteLine($"--{path}"); - String jsonArr = File.ReadAllText(path); - String ip = Path.GetFileName(path); - ip = ip.Substring(0, ip.Length - 4); - Debug.WriteLine($"-{ip}"); - Debug.WriteLine(jsonArr); - Connect(ip); - UIController.OnIncomingConnection(ip); - UIController.OnIncomingMessages(ip, jsonArr); - } - } - } - */ /// /// Saves incoming chat message to /// @@ -296,13 +268,9 @@ namespace PolyChat private void encode(string json) { - byte[] plaintext = Encoding.UTF8.GetBytes(json); - byte[] entropy = new byte[20]; - using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider()) - { - rng.GetBytes(entropy); - } - + String ecryptetText = FileManager.encrypt(json); + Debug.WriteLine(ecryptetText); + //Debug.WriteLine(FileManager.decrypt(ecryptetText)); } } } diff --git a/PolyChat/Models/FileManager.cs b/PolyChat/Models/FileManager.cs new file mode 100644 index 0000000..815873c --- /dev/null +++ b/PolyChat/Models/FileManager.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using System.Security.Cryptography; + + +namespace PolyChat.Models +{ + class FileManager + { + public static String encrypt(String toEncrypt) + { + try { + string textToEncrypt = toEncrypt; + string ToReturn = ""; + string publickey = "santhosh"; + string secretkey = "engineer"; + byte[] secretkeyByte = { }; + secretkeyByte = System.Text.Encoding.UTF8.GetBytes(secretkey); + byte[] publickeybyte = { }; + publickeybyte = System.Text.Encoding.UTF8.GetBytes(publickey); + MemoryStream ms = null; + CryptoStream cs = null; + byte[] inputbyteArray = System.Text.Encoding.UTF8.GetBytes(textToEncrypt); + using (DESCryptoServiceProvider des = new DESCryptoServiceProvider()) + { + ms = new MemoryStream(); + cs = new CryptoStream(ms, des.CreateEncryptor(publickeybyte, secretkeyByte), CryptoStreamMode.Write); + cs.Write(inputbyteArray, 0, inputbyteArray.Length); + cs.FlushFinalBlock(); + ToReturn = Convert.ToBase64String(ms.ToArray()); + } + return ToReturn; + } catch (Exception ex) + { + throw new Exception(ex.Message, ex.InnerException); + } + + } + + + public static String decrypt(String toDecrypt) + { + try + { + string textToDecrypt = toDecrypt; + string ToReturn = ""; + string publickey = "santhosh"; + string privatekey = "engineer"; + byte[] privatekeyByte = { }; + privatekeyByte = System.Text.Encoding.UTF8.GetBytes(privatekey); + byte[] publickeybyte = { }; + publickeybyte = System.Text.Encoding.UTF8.GetBytes(publickey); + MemoryStream ms = null; + CryptoStream cs = null; + byte[] inputbyteArray = new byte[textToDecrypt.Replace(" ", "+").Length]; + inputbyteArray = Convert.FromBase64String(textToDecrypt.Replace(" ", "+")); + using (DESCryptoServiceProvider des = new DESCryptoServiceProvider()) + { + ms = new MemoryStream(); + cs = new CryptoStream(ms, des.CreateDecryptor(publickeybyte, privatekeyByte), CryptoStreamMode.Write); + cs.Write(inputbyteArray, 0, inputbyteArray.Length); + cs.FlushFinalBlock(); + Encoding encoding = Encoding.UTF8; + ToReturn = encoding.GetString(ms.ToArray()); + } + return ToReturn; + } + catch (Exception ae) + { + throw new Exception(ae.Message, ae.InnerException); + } + } + } + + + + + + +} diff --git a/PolyChat/PolyChat.csproj b/PolyChat/PolyChat.csproj index 12041a4..c047509 100644 --- a/PolyChat/PolyChat.csproj +++ b/PolyChat/PolyChat.csproj @@ -137,6 +137,7 @@ + From 3cce36ee2144f95d86ad73646bed1319e19e6f83 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 14:02:42 +0200 Subject: [PATCH 28/82] Username change now dirty reactive in ui --- PolyChat/Controller.cs | 9 +++++++-- PolyChat/MainPage.xaml.cs | 16 ++++++++++++++-- PolyChat/Models/ChatPartner.cs | 15 ++------------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 989b9e2..975c7cc 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -37,9 +37,14 @@ namespace PolyChat { UIController = uiController; OwnIP = getIP(); - loadChats(); + //loadChats(); //SaveChats("10", "{das ist ein test}"); Serve(); + + // test + UIController.OnIncomingConnection("1.1.1.1"); + UIController.OnIncomingConnection("1.2.3.4"); + UIController.OnIncomingConnection("1.2.4.8"); } public void Connect(string ip) @@ -70,7 +75,7 @@ namespace PolyChat { Debug.WriteLine("--- Client connected! ---"); // setup event listeners - socket.On("initial", async (JToken[] data) => + socket.On("initial", (JToken[] data) => { Debug.WriteLine("--- initial packet received ---"); string ForeignIp = data[0].ToString(); diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 06a9db4..df1d05b 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -26,6 +26,7 @@ namespace PolyChat private ObservableCollection Partners; private ChatPartner selectedPartner = null; private string username; + public MainPage() { this.InitializeComponent(); @@ -80,6 +81,14 @@ namespace PolyChat public async void OnOpenNewChatDialog(object sender = null, RoutedEventArgs e = null) { + OnIncomingMessage( + "1.1.1.1", + new JObject( + new JProperty("type", "username"), + new JProperty("content", "Cloudflare") + ).ToString(), + DateTime.Now + ); NewChatDialog dialog = new NewChatDialog(); var result = await dialog.ShowAsync(); if (result == ContentDialogResult.Primary) @@ -138,7 +147,10 @@ namespace PolyChat { case "username": Debug.WriteLine($"! username change for {sendingPartner.Code} -> {content}"); - sendingPartner.SetName(Name); + sendingPartner.Name = content; + int index = Partners.IndexOf(sendingPartner); + Partners.Remove(sendingPartner); + Partners.Insert(index, sendingPartner); break; default: sendingPartner.AddMessage(new ChatMessage(origin, type, content, timeStamp)); @@ -159,7 +171,7 @@ namespace PolyChat origin, item["type"].ToString(), item["content"].ToString()//, - //DateTime.Parse(item["timestamp"].ToString()) + //DateTime.Parse(item["timestamp"].ToString()) ) ); } diff --git a/PolyChat/Models/ChatPartner.cs b/PolyChat/Models/ChatPartner.cs index 32df3be..abc5f00 100644 --- a/PolyChat/Models/ChatPartner.cs +++ b/PolyChat/Models/ChatPartner.cs @@ -1,17 +1,17 @@ using SocketIOSharp.Server.Client; using System.Collections.ObjectModel; using System.ComponentModel; +using System.Diagnostics; using System.Runtime.CompilerServices; namespace PolyChat.Models { - public class ChatPartner : INotifyPropertyChanged + public class ChatPartner { public string Name; public string Code; public ObservableCollection Messages; private SocketIOSocket socketIOSocket; - public event PropertyChangedEventHandler PropertyChanged; public ChatPartner(string name, string code, ObservableCollection messages = null) { @@ -23,20 +23,9 @@ namespace PolyChat.Models public SocketIOSocket SocketIOSocket { get => socketIOSocket; set => socketIOSocket = value; } - private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - public void AddMessage(ChatMessage message) { Messages.Add(message); } - - public void SetName(string name) - { - Name = name; - NotifyPropertyChanged("Name"); - } } } \ No newline at end of file From e5cbd588295a5ec374c2a1bb7271c483979c1aac Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 14:04:39 +0200 Subject: [PATCH 29/82] removed test code for username reactivitiy --- PolyChat/Controller.cs | 2 ++ PolyChat/MainPage.xaml.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index f27f2ff..8d35d15 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -44,9 +44,11 @@ namespace PolyChat Serve(); // test + /* UIController.OnIncomingConnection("1.1.1.1"); UIController.OnIncomingConnection("1.2.3.4"); UIController.OnIncomingConnection("1.2.4.8"); + */ } public void Connect(string ip) diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 5179fbb..c269543 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -81,6 +81,7 @@ namespace PolyChat public async void OnOpenNewChatDialog(object sender = null, RoutedEventArgs e = null) { + /* OnIncomingMessage( "1.1.1.1", new JObject( @@ -89,6 +90,7 @@ namespace PolyChat ).ToString(), DateTime.Now ); + */ NewChatDialog dialog = new NewChatDialog(); var result = await SafelyOpenDialog(dialog); if (result == ContentDialogResult.Primary) From ebabd8036dd13f0557b1f6e44b6ddff596b59f56 Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 14:07:07 +0200 Subject: [PATCH 30/82] Propper file manager with encryption of files --- PolyChat/Controller.cs | 106 ++------------------------- PolyChat/Models/FileManager.cs | 127 ++++++++++++++++++++++++++++++++- 2 files changed, 131 insertions(+), 102 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 8d35d15..f9c8131 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -26,6 +26,7 @@ namespace PolyChat private const ushort PORT = 8050; // Controller private readonly MainPage UIController; + private readonly FileManager fileManager; // Props private Dictionary Connections = new Dictionary(); private string OwnName = ""; @@ -38,9 +39,9 @@ namespace PolyChat public Controller(MainPage uiController) { UIController = uiController; + fileManager = new FileManager(uiController); OwnIP = getIP(); - loadChats(); - encode("test"); + fileManager.loadChats(); Serve(); // test @@ -120,7 +121,7 @@ namespace PolyChat // send as json Connections[ip].SendMessage(json.ToString()); // save to logs - SaveChats(ip, json.ToString(), DateTime.Now); + fileManager.saveChats(ip, json.ToString(), DateTime.Now); } private void OnMessage(string ip, JToken[] data) @@ -131,7 +132,7 @@ namespace PolyChat DateTime now = DateTime.Now; Debug.WriteLine("RAW: " + data[0]); UIController.OnIncomingMessage(ip, data[0].ToString(), now); - SaveChats(ip, data[0].ToString(), now); + fileManager.saveChats(ip, data[0].ToString(), now); } else Debug.WriteLine("Undefined: " + data); } @@ -177,106 +178,11 @@ namespace PolyChat return null; } - /// - /// sends chatlogs as json array to uiController wit corrosponding ip - /// - /// in ui when chat is clicked connection gets established - /// - /// - public void loadChats() - { - //TODO: also load chatlogs when user tries to connect - //load dir and create if non existant - if (Directory.Exists("U:\\PolyChat\\Saves")) - { - Debug.WriteLine("--Path exists.--"); - } - else - { - Directory.CreateDirectory("U:\\PolyChat\\Saves"); - Debug.WriteLine("--Path Created--."); - } - - //go through all files and send ip and json array to ui - String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); - if (filepaths.Length > 0) - { - Debug.WriteLine("---Loading Saves"); - foreach (String path in filepaths) - { - Debug.WriteLine($"--{path}"); - String jsonArr = File.ReadAllText(path); - String ip = Path.GetFileName(path); - ip = ip.Substring(0, ip.Length - 4); - Debug.WriteLine($"-{ip}"); - Debug.WriteLine(jsonArr); - UIController.OnIncomingConnection(ip); - UIController.OnIncomingMessages(ip, jsonArr); - } - } - } - - /// - /// Saves incoming chat message to - /// - /// - /// - public void SaveChats(string ip, string json, DateTime timeStamp) - { - //Vielleicht noch so machen dass die mit gleicher ip nacheinander gemacht - //werden damit es nicht zu überschreibungen kommt vielleicth auch ganz oben oder am ende ne - //writing flag setzen oder auch in der datei selbst ne flag setzen - new Thread(() => - { - //breaking if namechange - JObject obj = JObject.Parse(json); - if (!obj["type"].ToString().Equals("username")) - { - //adding timestamp - obj = JObject.Parse(json); - obj.Add(new JProperty("timestamp", timeStamp)); - json = obj.ToString(); - if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) - { - Debug.WriteLine("--File allready exists--"); - //check for integraty of file - string output = File.ReadAllText($"U:\\PolyChat\\Saves\\{ip}.txt"); - Debug.WriteLine($"---{output}---"); - if (output.Substring(0, 1).Equals("[") && output.Substring(output.Length - 1, 1).Equals("]")) - { - Debug.WriteLine("--adding new chatmessage--"); - //structure intact - //save new chat - String saved = output.Substring(0, output.Length - 1); - output = saved + ", " + json + " ]"; - File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); - File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", output); - } - else - { - Debug.WriteLine("--Structure not intact--"); - Debug.WriteLine("--redoing file--"); - //structure not intact - //redo file - File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); - File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", $"[ {json} ]"); - } - } - else - { - Debug.WriteLine("--Creating new File--"); - //setup file - File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", $"[ {json} ]"); - } - } - }).Start(); - } - private void encode(string json) { String ecryptetText = FileManager.encrypt(json); Debug.WriteLine(ecryptetText); - //Debug.WriteLine(FileManager.decrypt(ecryptetText)); + Debug.WriteLine(FileManager.decrypt(ecryptetText)); } } } diff --git a/PolyChat/Models/FileManager.cs b/PolyChat/Models/FileManager.cs index 815873c..3702fd5 100644 --- a/PolyChat/Models/FileManager.cs +++ b/PolyChat/Models/FileManager.cs @@ -5,12 +5,131 @@ using System.Text; using System.Threading.Tasks; using System.IO; using System.Security.Cryptography; - +using System.Diagnostics; +using System.Threading; +using Newtonsoft.Json.Linq; namespace PolyChat.Models { class FileManager { + // Controller + private readonly MainPage UIController; + + public FileManager(MainPage uiController) + { + UIController = uiController; + } + + /// + /// sends chatlogs as json array to uiController wit corrosponding ip + /// + /// in ui when chat is clicked connection gets established + /// + /// + public void loadChats() + { + //load dir and create if non existant + if (Directory.Exists("U:\\PolyChat\\Saves")) + { + Debug.WriteLine("--Path exists.--"); + } + else + { + Directory.CreateDirectory("U:\\PolyChat\\Saves"); + Debug.WriteLine("--Path Created--."); + } + + //go through all files and send ip and json array to ui + String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); + if (filepaths.Length > 0) + { + Debug.WriteLine("---Loading Saves"); + foreach (String path in filepaths) + { + Debug.WriteLine($"--{path}"); + String jsonArr = decrypt(File.ReadAllText(path)); + String ip = Path.GetFileName(path); + ip = ip.Substring(0, ip.Length - 4); + Debug.WriteLine($"-{ip}"); + Debug.WriteLine(jsonArr); + UIController.OnIncomingConnection(ip); + UIController.OnIncomingMessages(ip, jsonArr); + } + } + } + + /// + /// Saves incoming chat message to + /// + /// + /// + public void saveChats(string ip, string json, DateTime timeStamp) + { + //Vielleicht noch so machen dass die mit gleicher ip nacheinander gemacht + //werden damit es nicht zu überschreibungen kommt vielleicth auch ganz oben oder am ende ne + //writing flag setzen oder auch in der datei selbst ne flag setzen + new Thread(() => + { + //breaking if namechange + JObject obj = JObject.Parse(json); + if (!obj["type"].ToString().Equals("username")) + { + //adding timestamp + obj = JObject.Parse(json); + obj.Add(new JProperty("timestamp", timeStamp)); + json = obj.ToString(); + if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) + { + Debug.WriteLine("--File allready exists--"); + //check for integraty of file + string output = decrypt(File.ReadAllText($"U:\\PolyChat\\Saves\\{ip}.txt")); + Debug.WriteLine($"---{output}---"); + if (output.Substring(0, 1).Equals("[") && output.Substring(output.Length - 1, 1).Equals("]")) + { + Debug.WriteLine("--adding new chatmessage--"); + //structure intact + //save new chat + String saved = output.Substring(0, output.Length - 1); + output = saved + ", " + json + " ]"; + File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", encrypt(output)); + } + else + { + Debug.WriteLine("--Structure not intact--"); + Debug.WriteLine("--redoing file--"); + //structure not intact + //redo file + File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", encrypt($"[ {json} ]")); + } + } + else + { + Debug.WriteLine("--Creating new File--"); + //setup file + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", encrypt($"[ {json} ]")); + } + } + }).Start(); + } + + + //--------------------------------------------------------------------------------------------------- + //security + //--------------------------------------------------------------------------------------------------- + private void genKeys() + { + + } + + + /// + /// does exactly what it says. XD + /// + /// + /// public static String encrypt(String toEncrypt) { try { @@ -41,7 +160,11 @@ namespace PolyChat.Models } - + /// + /// does exactly what it says. XD + /// + /// + /// public static String decrypt(String toDecrypt) { try From 2ec9ffb30f88fd667c116e0e22e0c229cbaaf5e9 Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 14:26:35 +0200 Subject: [PATCH 31/82] Added method to delete userspeciffic logs. --- PolyChat/Controller.cs | 2 ++ PolyChat/Models/FileManager.cs | 61 ++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index f9c8131..d7a2d6f 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -93,8 +93,10 @@ namespace PolyChat } else { + Debug.WriteLine("---- Added new Connection ----");//Todo show error! Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data), CloseChat)); UIController.OnIncomingConnection(ForeignIp); + fileManager.loadChats(ForeignIp); } }); }); diff --git a/PolyChat/Models/FileManager.cs b/PolyChat/Models/FileManager.cs index 3702fd5..ec1485f 100644 --- a/PolyChat/Models/FileManager.cs +++ b/PolyChat/Models/FileManager.cs @@ -21,6 +21,67 @@ namespace PolyChat.Models UIController = uiController; } + /// + /// deletes chatlog of one speciffic user + /// + /// + public void deleteChat(String ip) + { + if (Directory.Exists("U:\\PolyChat\\Saves")) + { + Debug.WriteLine("--Path exists.--"); + //go through all files and send ip and json array to ui + String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); + if (filepaths.Length > 0) + { + foreach (String path in filepaths) + { + if(Path.GetFileName(path).Equals(ip)) + { + File.Delete(path); + return; + } + } + } + } + } + + /// + /// loads one chatlog probably when someone tries to connect + /// + /// + public void loadChats() + { + //load dir and create if non existant + if (Directory.Exists("U:\\PolyChat\\Saves")) + { + Debug.WriteLine("--Path exists.--"); + } + else + { + Directory.CreateDirectory("U:\\PolyChat\\Saves"); + Debug.WriteLine("--Path Created--."); + } + + //go through all files and send ip and json array to ui + String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); + if (filepaths.Length > 0) + { + Debug.WriteLine("---Loading Saves"); + foreach (String path in filepaths) + { + Debug.WriteLine($"--{path}"); + String jsonArr = decrypt(File.ReadAllText(path)); + String ip = Path.GetFileName(path); + ip = ip.Substring(0, ip.Length - 4); + Debug.WriteLine($"-{ip}"); + Debug.WriteLine(jsonArr); + UIController.OnIncomingConnection(ip); + UIController.OnIncomingMessages(ip, jsonArr); + } + } + } + /// /// sends chatlogs as json array to uiController wit corrosponding ip /// From 154f79667c891750d5d1012682f48619fa328cc7 Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 14:32:22 +0200 Subject: [PATCH 32/82] load chatlogs on newly created chat --- PolyChat/Controller.cs | 4 ++-- PolyChat/Models/FileManager.cs | 37 ++++++++++------------------------ 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index d7a2d6f..1ec8a3e 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -93,10 +93,10 @@ namespace PolyChat } else { - Debug.WriteLine("---- Added new Connection ----");//Todo show error! + Debug.WriteLine("---- Added new Connection ----"); Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data), CloseChat)); UIController.OnIncomingConnection(ForeignIp); - fileManager.loadChats(ForeignIp); + fileManager.loadChat(ForeignIp); } }); }); diff --git a/PolyChat/Models/FileManager.cs b/PolyChat/Models/FileManager.cs index ec1485f..474aee2 100644 --- a/PolyChat/Models/FileManager.cs +++ b/PolyChat/Models/FileManager.cs @@ -36,7 +36,7 @@ namespace PolyChat.Models { foreach (String path in filepaths) { - if(Path.GetFileName(path).Equals(ip)) + if (Path.GetFileName(path).Equals(ip)) { File.Delete(path); return; @@ -50,32 +50,15 @@ namespace PolyChat.Models /// loads one chatlog probably when someone tries to connect /// /// - public void loadChats() + public void loadChat(String ip) { //load dir and create if non existant if (Directory.Exists("U:\\PolyChat\\Saves")) { Debug.WriteLine("--Path exists.--"); - } - else - { - Directory.CreateDirectory("U:\\PolyChat\\Saves"); - Debug.WriteLine("--Path Created--."); - } - - //go through all files and send ip and json array to ui - String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); - if (filepaths.Length > 0) - { - Debug.WriteLine("---Loading Saves"); - foreach (String path in filepaths) + if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) { - Debug.WriteLine($"--{path}"); - String jsonArr = decrypt(File.ReadAllText(path)); - String ip = Path.GetFileName(path); - ip = ip.Substring(0, ip.Length - 4); - Debug.WriteLine($"-{ip}"); - Debug.WriteLine(jsonArr); + String jsonArr = decrypt(File.ReadAllText($"U:\\PolyChat\\Saves\\{ip}.txt")); UIController.OnIncomingConnection(ip); UIController.OnIncomingMessages(ip, jsonArr); } @@ -180,9 +163,9 @@ namespace PolyChat.Models //--------------------------------------------------------------------------------------------------- //security //--------------------------------------------------------------------------------------------------- - private void genKeys() - { - + private void genKeys() + { + } @@ -193,7 +176,8 @@ namespace PolyChat.Models /// public static String encrypt(String toEncrypt) { - try { + try + { string textToEncrypt = toEncrypt; string ToReturn = ""; string publickey = "santhosh"; @@ -214,7 +198,8 @@ namespace PolyChat.Models ToReturn = Convert.ToBase64String(ms.ToArray()); } return ToReturn; - } catch (Exception ex) + } + catch (Exception ex) { throw new Exception(ex.Message, ex.InnerException); } From 34a31794922fe390a31edb67adc03e6a14def9f9 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 14:36:21 +0200 Subject: [PATCH 33/82] Dark Theme, added styling for messages --- PolyChat/MainPage.xaml | 12 ++++++------ PolyChat/MainPage.xaml.cs | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/PolyChat/MainPage.xaml b/PolyChat/MainPage.xaml index e2d66c4..596ea2b 100644 --- a/PolyChat/MainPage.xaml +++ b/PolyChat/MainPage.xaml @@ -6,7 +6,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:models="using:PolyChat.Models" mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" RequestedTheme="Dark"> @@ -97,13 +97,13 @@ - + - - - - + + + + diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index c269543..752a608 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -81,6 +81,7 @@ namespace PolyChat public async void OnOpenNewChatDialog(object sender = null, RoutedEventArgs e = null) { + // test /* OnIncomingMessage( "1.1.1.1", From 4e60fd2bdb6537a8b7d6be888fcb15f649ab4bff Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 14:50:35 +0200 Subject: [PATCH 34/82] delete chat only on explicit button press, not on disconnect, dialog opening fixed --- PolyChat/Controller.cs | 13 ++++++---- PolyChat/MainPage.xaml.cs | 43 +++++++++++++++++----------------- PolyChat/Models/Connection.cs | 8 +++---- PolyChat/Models/FileManager.cs | 2 +- PolyChat/Views/Dialog.xaml.cs | 2 -- 5 files changed, 35 insertions(+), 33 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 1ec8a3e..ce10213 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -96,7 +96,6 @@ namespace PolyChat Debug.WriteLine("---- Added new Connection ----"); Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data), CloseChat)); UIController.OnIncomingConnection(ForeignIp); - fileManager.loadChat(ForeignIp); } }); }); @@ -139,7 +138,7 @@ namespace PolyChat else Debug.WriteLine("Undefined: " + data); } - public void CloseChat(string IP, bool wasConnected = true) + public void CloseChat(string IP, bool wasConnected = true, bool delete = false) { Debug.WriteLine($"Deleting connection with IP:{IP}"); if (IP != null && Connections.ContainsKey(IP)) @@ -147,14 +146,18 @@ namespace PolyChat Connections[IP].Close(); Connections.Remove(IP); } - CloseChatUI(IP, wasConnected); + if (delete || !wasConnected) + CloseChatUI(IP, wasConnected, delete); + if (delete) + fileManager.deleteChat(IP); } - private void CloseChatUI(string IP, bool wasConnected = true) + private void CloseChatUI(string IP, bool wasConnected = true, bool delete = false) { UIController.OnChatPartnerDeleted(IP); string heading = wasConnected ? "Connection Closed" : "Connection Failed"; - UIController.ShowConnectionError(IP, heading, $"Connecting to {IP} failed..."); + if(!delete) + UIController.ShowConnectionError(IP, heading, $"Connecting to {IP} failed..."); } private bool isInConnections(string IP) diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 752a608..59ef3c3 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -46,26 +46,27 @@ namespace PolyChat await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { Dialog dialog = new Dialog( - Dialog.TYPE_ERROR, - heading, - message, - new DialogButton( - "Retry", - () => - { - Controller.Connect(param); - Partners.Add(new ChatPartner( - "Connecting...", - param - )); - updateNoChatsPlaceholder(); - } - ), - new DialogButton( - "Ignore", - () => { /* do nothing */ } - ) - ); + Dialog.TYPE_ERROR, + heading, + message, + new DialogButton( + "Retry", + () => + { + Controller.Connect(param); + Partners.Add(new ChatPartner( + "Connecting...", + param + )); + updateNoChatsPlaceholder(); + } + ), + new DialogButton( + "Ignore", + () => { /* do nothing */ } + ) + ); + SafelyOpenDialog(dialog); }); } @@ -184,7 +185,7 @@ namespace PolyChat private void OnDeleteChat(object sender = null, RoutedEventArgs e = null) { - Controller.CloseChat(selectedPartner.Code); + Controller.CloseChat(selectedPartner.Code,delete: true); Partners.Remove(selectedPartner); updateNoChatsPlaceholder(); updateNoChatSelected(); diff --git a/PolyChat/Models/Connection.cs b/PolyChat/Models/Connection.cs index 9c985b6..5d45440 100644 --- a/PolyChat/Models/Connection.cs +++ b/PolyChat/Models/Connection.cs @@ -15,9 +15,9 @@ namespace PolyChat.Models private SocketIOSocket Socket; private bool Connected = false; private readonly string IP; - private Action DeleteConnection; + private Action DeleteConnection; - public Connection(string ip, ushort port, Action onMessage, Action onClose) + public Connection(string ip, ushort port, Action onMessage, Action onClose) { Debug.WriteLine("! CONNECTING TO SERVER !"); IP = ip; @@ -32,7 +32,7 @@ namespace PolyChat.Models Client.On("message", (Action) onMessage); } - public Connection(SocketIOSocket socket, Action onMessage, Action onClose) + public Connection(SocketIOSocket socket, Action onMessage, Action onClose) { Socket = socket; DeleteConnection = onClose; @@ -66,7 +66,7 @@ namespace PolyChat.Models { Debug.WriteLine("--- Disconnected! ---"); Debug.WriteLine($"--- Deleting Connection with IP: {IP}---"); - DeleteConnection(IP, IsConnected()); + DeleteConnection(IP, IsConnected(),false); Connected = false; } private void OnError(JToken[] data) diff --git a/PolyChat/Models/FileManager.cs b/PolyChat/Models/FileManager.cs index 474aee2..9a8045e 100644 --- a/PolyChat/Models/FileManager.cs +++ b/PolyChat/Models/FileManager.cs @@ -36,7 +36,7 @@ namespace PolyChat.Models { foreach (String path in filepaths) { - if (Path.GetFileName(path).Equals(ip)) + if (Path.GetFileName(path).Equals(ip+".txt")) { File.Delete(path); return; diff --git a/PolyChat/Views/Dialog.xaml.cs b/PolyChat/Views/Dialog.xaml.cs index 331fb6f..6e1ce68 100644 --- a/PolyChat/Views/Dialog.xaml.cs +++ b/PolyChat/Views/Dialog.xaml.cs @@ -34,8 +34,6 @@ namespace PolyChat.Views // TODO: use event handlers and asign actions here Primary = primary.Action; Secondary = secondary.Action; - // show - MainPage.SafelyOpenDialog(this); } private void setType(string type, string message) From 15679951bcb208d8e82f553fdf6a97170528a2d3 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 14:54:25 +0200 Subject: [PATCH 35/82] removed ability to send empty messages --- PolyChat/MainPage.xaml.cs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 752a608..1b6f6e2 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -213,33 +213,19 @@ namespace PolyChat private void OnKeyUp(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e) { updateSendButtonEnabled(); - if (e.Key == Windows.System.VirtualKey.Enter) + if (buttonSend.IsEnabled && e.Key == Windows.System.VirtualKey.Enter) { OnSendMessage(); } } - public static IAsyncOperation SafelyOpenDialog(Dialog d) + public static IAsyncOperation SafelyOpenDialog(ContentDialog d) { if(VisualTreeHelper.GetOpenPopups(Window.Current).Count == 0) return d.ShowAsync(); return null; } - public static IAsyncOperation SafelyOpenDialog(NewChatDialog d) - { - if (VisualTreeHelper.GetOpenPopups(Window.Current).Count == 0) - return d.ShowAsync(); - return null; - } - - public static IAsyncOperation SafelyOpenDialog(EditUsernameDialog d) - { - if (VisualTreeHelper.GetOpenPopups(Window.Current).Count == 0) - return d.ShowAsync(); - return null; - } - // UPDATE FUNCTIONS FOR UI PLACEHOLDERS private void updateNoChatsPlaceholder() From 8e17ffa978879c3ce7b4f5e0aa4cb87f06a59f07 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 15:18:57 +0200 Subject: [PATCH 36/82] dont open new chat window if client is already in list --- PolyChat/MainPage.xaml.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 59ef3c3..452e752 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -99,7 +99,9 @@ namespace PolyChat { string ip = IP.GetIPFromCode(dialog.getValue()); Controller.Connect(ip); - Partners.Add(new ChatPartner( + ChatPartner pa = Partners.FirstOrDefault(p => p.Code == ip); + if (pa == null) + Partners.Add(new ChatPartner( "Connecting...", ip )); From de2d135fdfaa80b58c78b80f2a2a4af9706d557c Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 15:31:30 +0200 Subject: [PATCH 37/82] Added Themes + Toggle --- PolyChat/MainPage.xaml | 16 +++++++--------- PolyChat/MainPage.xaml.cs | 16 +++++++++++++++- PolyChat/Views/Dialog.xaml.cs | 1 + PolyChat/Views/EditUsernameDialog.xaml.cs | 1 + PolyChat/Views/NewChatDialog.xaml.cs | 1 + 5 files changed, 25 insertions(+), 10 deletions(-) diff --git a/PolyChat/MainPage.xaml b/PolyChat/MainPage.xaml index 596ea2b..3ec2199 100644 --- a/PolyChat/MainPage.xaml +++ b/PolyChat/MainPage.xaml @@ -6,17 +6,18 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:models="using:PolyChat.Models" mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" RequestedTheme="Dark"> + Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> - + + @@ -37,7 +38,7 @@ - + @@ -55,12 +56,8 @@ - - - - - - + + diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 1b6f6e2..d9286c2 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -26,6 +26,8 @@ namespace PolyChat private ObservableCollection Partners; private ChatPartner selectedPartner = null; private string username; + private static ElementTheme Theme = ElementTheme.Light; + public MainPage() { @@ -35,6 +37,9 @@ namespace PolyChat // ui variables ipAddress.Text = IP.GetCodeFromIP(Controller.getIP()); Partners = new ObservableCollection(); + // theming + RequestedTheme = Theme; + // updated placeholder updateNoChatsPlaceholder(); updateNoUsernamePlaceholder(); updateNoChatSelected(); @@ -181,7 +186,6 @@ namespace PolyChat }); } - private void OnDeleteChat(object sender = null, RoutedEventArgs e = null) { Controller.CloseChat(selectedPartner.Code); @@ -210,6 +214,12 @@ namespace PolyChat if (!Controller.IsConnected(code)) Controller.Connect(code); } + public void OnToggleTheme(object sender, RoutedEventArgs e) + { + Theme = Theme == ElementTheme.Light ? ElementTheme.Dark : ElementTheme.Light; + RequestedTheme = Theme; + } + private void OnKeyUp(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e) { updateSendButtonEnabled(); @@ -226,6 +236,10 @@ namespace PolyChat return null; } + // GETTERS + + public static ElementTheme GetTheme() => Theme; + // UPDATE FUNCTIONS FOR UI PLACEHOLDERS private void updateNoChatsPlaceholder() diff --git a/PolyChat/Views/Dialog.xaml.cs b/PolyChat/Views/Dialog.xaml.cs index 331fb6f..75dd155 100644 --- a/PolyChat/Views/Dialog.xaml.cs +++ b/PolyChat/Views/Dialog.xaml.cs @@ -36,6 +36,7 @@ namespace PolyChat.Views Secondary = secondary.Action; // show MainPage.SafelyOpenDialog(this); + RequestedTheme = MainPage.GetTheme(); } private void setType(string type, string message) diff --git a/PolyChat/Views/EditUsernameDialog.xaml.cs b/PolyChat/Views/EditUsernameDialog.xaml.cs index faec438..8e44c1e 100644 --- a/PolyChat/Views/EditUsernameDialog.xaml.cs +++ b/PolyChat/Views/EditUsernameDialog.xaml.cs @@ -22,6 +22,7 @@ namespace PolyChat.Views if (initialValue == null || initialValue.Length == 0) IsSecondaryButtonEnabled = false; else input.Text = initialValue; validate(); + RequestedTheme = MainPage.GetTheme(); } public string getValue() diff --git a/PolyChat/Views/NewChatDialog.xaml.cs b/PolyChat/Views/NewChatDialog.xaml.cs index 4c888ba..a77d96f 100644 --- a/PolyChat/Views/NewChatDialog.xaml.cs +++ b/PolyChat/Views/NewChatDialog.xaml.cs @@ -20,6 +20,7 @@ namespace PolyChat.Views { this.InitializeComponent(); IsPrimaryButtonEnabled = false; + RequestedTheme = MainPage.GetTheme(); } public string getValue() From a5dd048f076cc5a14c7bdf01cbc790527ec88057 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 15:46:13 +0200 Subject: [PATCH 38/82] remove unused variables --- PolyChat/Controller.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index ce10213..d5e6efe 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -29,8 +29,6 @@ namespace PolyChat private readonly FileManager fileManager; // Props private Dictionary Connections = new Dictionary(); - private string OwnName = ""; - private string OwnIP; /// /// Initializes Controller with UI access @@ -40,7 +38,6 @@ namespace PolyChat { UIController = uiController; fileManager = new FileManager(uiController); - OwnIP = getIP(); fileManager.loadChats(); Serve(); From 88e5d3a3297020799cbfb51be9ba4ea7f4efb963 Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 16:03:12 +0200 Subject: [PATCH 39/82] Cleanup / Comments --- PolyChat/Controller.cs | 48 +++++++++++++++---- PolyChat/Models/FileManager.cs | 85 ++++++++++++++++++---------------- 2 files changed, 83 insertions(+), 50 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index ce10213..ea71b49 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -15,8 +15,7 @@ using Windows.Security.Cryptography; using Windows.Storage.Streams; namespace PolyChat -{ - +{ // 10.1.211.26 Marc // 10.1.218.90 Felix // 10.4.141.77 Pat @@ -39,7 +38,7 @@ namespace PolyChat public Controller(MainPage uiController) { UIController = uiController; - fileManager = new FileManager(uiController); + fileManager = new FileManager(this); OwnIP = getIP(); fileManager.loadChats(); Serve(); @@ -68,6 +67,9 @@ namespace PolyChat } + /// + /// starts server for clients to connect to + /// private void Serve() { Debug.WriteLine("--- Controller.Serve ---"); @@ -110,6 +112,12 @@ namespace PolyChat } } + /// + /// Sends message to given ip + /// + /// + /// + /// public void SendMessage(string ip, string type, string content) { Debug.WriteLine("--- Controller.SendMessage ---"); @@ -124,7 +132,12 @@ namespace PolyChat // save to logs fileManager.saveChats(ip, json.ToString(), DateTime.Now); } - + + /// + /// if We recieve a message this method gets triggert + /// + /// ip from sender + /// String that is send private void OnMessage(string ip, JToken[] data) { Debug.WriteLine("--- Controller.OnMessage ---"); @@ -138,6 +151,12 @@ namespace PolyChat else Debug.WriteLine("Undefined: " + data); } + /// + /// Closes chat connection + /// + /// ip of user to be closed + /// + /// public void CloseChat(string IP, bool wasConnected = true, bool delete = false) { Debug.WriteLine($"Deleting connection with IP:{IP}"); @@ -152,6 +171,17 @@ namespace PolyChat fileManager.deleteChat(IP); } + /// + /// sends incoming message to ui + /// + /// ip of client that send the message + /// the json array that is ti be displayed in th gui + public void SendIncomingMessageUI(String ip, String jsonArr) + { + UIController.OnIncomingConnection(ip); + UIController.OnIncomingMessages(ip, jsonArr); + } + private void CloseChatUI(string IP, bool wasConnected = true, bool delete = false) { UIController.OnChatPartnerDeleted(IP); @@ -169,6 +199,10 @@ namespace PolyChat return Connections.ContainsKey(ip) && Connections[ip].IsConnected(); } + /// + /// returns your own ip that starts with 10. becuase that is our subnet + /// + /// public static string getIP() { IPHostEntry ipEntry = Dns.GetHostEntry(Dns.GetHostName()); @@ -183,11 +217,5 @@ namespace PolyChat return null; } - private void encode(string json) - { - String ecryptetText = FileManager.encrypt(json); - Debug.WriteLine(ecryptetText); - Debug.WriteLine(FileManager.decrypt(ecryptetText)); - } } } diff --git a/PolyChat/Models/FileManager.cs b/PolyChat/Models/FileManager.cs index 9a8045e..e841c29 100644 --- a/PolyChat/Models/FileManager.cs +++ b/PolyChat/Models/FileManager.cs @@ -14,13 +14,21 @@ namespace PolyChat.Models class FileManager { // Controller - private readonly MainPage UIController; + private readonly Controller controller; - public FileManager(MainPage uiController) + + //=============================================================================================================================================== + //Constructor + //=============================================================================================================================================== + public FileManager(Controller controller) { - UIController = uiController; + this.controller = controller; } + //=============================================================================================================================================== + // editing save files + //=============================================================================================================================================== + /// /// deletes chatlog of one speciffic user /// @@ -59,14 +67,13 @@ namespace PolyChat.Models if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) { String jsonArr = decrypt(File.ReadAllText($"U:\\PolyChat\\Saves\\{ip}.txt")); - UIController.OnIncomingConnection(ip); - UIController.OnIncomingMessages(ip, jsonArr); + controller.SendIncomingMessageUI(ip, jsonArr); } } } /// - /// sends chatlogs as json array to uiController wit corrosponding ip + /// sends chatlogs as json array to uiController with corrosponding ip /// /// in ui when chat is clicked connection gets established /// @@ -97,22 +104,18 @@ namespace PolyChat.Models ip = ip.Substring(0, ip.Length - 4); Debug.WriteLine($"-{ip}"); Debug.WriteLine(jsonArr); - UIController.OnIncomingConnection(ip); - UIController.OnIncomingMessages(ip, jsonArr); + controller.SendIncomingMessageUI(ip, jsonArr); } } } /// - /// Saves incoming chat message to + /// Saves incoming chat message to U:\PolyChat\Saves\ip.txt /// /// /// public void saveChats(string ip, string json, DateTime timeStamp) { - //Vielleicht noch so machen dass die mit gleicher ip nacheinander gemacht - //werden damit es nicht zu überschreibungen kommt vielleicth auch ganz oben oder am ende ne - //writing flag setzen oder auch in der datei selbst ne flag setzen new Thread(() => { //breaking if namechange @@ -120,39 +123,39 @@ namespace PolyChat.Models if (!obj["type"].ToString().Equals("username")) { //adding timestamp - obj = JObject.Parse(json); obj.Add(new JProperty("timestamp", timeStamp)); json = obj.ToString(); + if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) { Debug.WriteLine("--File allready exists--"); + //check for integraty of file string output = decrypt(File.ReadAllText($"U:\\PolyChat\\Saves\\{ip}.txt")); Debug.WriteLine($"---{output}---"); if (output.Substring(0, 1).Equals("[") && output.Substring(output.Length - 1, 1).Equals("]")) { - Debug.WriteLine("--adding new chatmessage--"); //structure intact //save new chat - String saved = output.Substring(0, output.Length - 1); - output = saved + ", " + json + " ]"; + Debug.WriteLine("--adding new chatmessage--"); + output = output.Substring(0, output.Length - 1) + ", " + json + " ]"; //rip appart and put file back together File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); - File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", encrypt(output)); + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", encrypt(output)); //encrypt and save to textfile } else { - Debug.WriteLine("--Structure not intact--"); - Debug.WriteLine("--redoing file--"); //structure not intact //redo file + Debug.WriteLine("--Structure not intact--"); + Debug.WriteLine("--redoing file--"); File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); - File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", encrypt($"[ {json} ]")); + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", encrypt($"[ {json} ]")); //encrypt and write to file } } else { - Debug.WriteLine("--Creating new File--"); //setup file + Debug.WriteLine("--Creating new File--"); File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", encrypt($"[ {json} ]")); } } @@ -160,28 +163,35 @@ namespace PolyChat.Models } - //--------------------------------------------------------------------------------------------------- - //security - //--------------------------------------------------------------------------------------------------- - private void genKeys() + //=============================================================================================================================================== + // Encryption + //=============================================================================================================================================== + + /// + /// generates keypair [public, privte] (100% secure, trust me) + /// + /// + private String[] genKeys() { - + return new String[] {"sehrSichererKey", "nochVielSichererKey"}; } - + /// /// does exactly what it says. XD + /// + /// encrypts given string and returns unreadable string /// /// /// - public static String encrypt(String toEncrypt) + private String encrypt(String toEncrypt) { try { string textToEncrypt = toEncrypt; string ToReturn = ""; - string publickey = "santhosh"; - string secretkey = "engineer"; + string publickey = genKeys()[0]; + string secretkey = genKeys()[1]; byte[] secretkeyByte = { }; secretkeyByte = System.Text.Encoding.UTF8.GetBytes(secretkey); byte[] publickeybyte = { }; @@ -203,22 +213,23 @@ namespace PolyChat.Models { throw new Exception(ex.Message, ex.InnerException); } - } /// /// does exactly what it says. XD + /// + /// takes in unreadable string and returns infirmation /// /// /// - public static String decrypt(String toDecrypt) + private String decrypt(String toDecrypt) { try { string textToDecrypt = toDecrypt; string ToReturn = ""; - string publickey = "santhosh"; - string privatekey = "engineer"; + string publickey = genKeys()[0]; + string privatekey = genKeys()[1]; byte[] privatekeyByte = { }; privatekeyByte = System.Text.Encoding.UTF8.GetBytes(privatekey); byte[] publickeybyte = { }; @@ -244,10 +255,4 @@ namespace PolyChat.Models } } } - - - - - - } From 0a1ff66e389d49e0ff826d20dfd62ed3deb6ce87 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 16:05:20 +0200 Subject: [PATCH 40/82] Message Alignment First Try --- PolyChat/MainPage.xaml | 9 +++++---- PolyChat/MainPage.xaml.cs | 10 +++++----- PolyChat/Models/ChatMessage.cs | 26 ++++++-------------------- 3 files changed, 16 insertions(+), 29 deletions(-) diff --git a/PolyChat/MainPage.xaml b/PolyChat/MainPage.xaml index 3ec2199..1ed214d 100644 --- a/PolyChat/MainPage.xaml +++ b/PolyChat/MainPage.xaml @@ -98,10 +98,11 @@ - - - - + + + + + diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index d9286c2..77d97b7 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -28,7 +28,6 @@ namespace PolyChat private string username; private static ElementTheme Theme = ElementTheme.Light; - public MainPage() { this.InitializeComponent(); @@ -78,7 +77,7 @@ namespace PolyChat public void OnSendMessage(object sender = null, RoutedEventArgs e = null) { - selectedPartner.AddMessage(new ChatMessage(username, "message", inputSend.Text)); + selectedPartner.AddMessage(new ChatMessage(username, "message", inputSend.Text, DateTime.Now, false)); Controller.SendMessage(selectedPartner.Code, "message", inputSend.Text); // clear input inputSend.Text = ""; @@ -161,7 +160,7 @@ namespace PolyChat Partners.Insert(index, sendingPartner); break; default: - sendingPartner.AddMessage(new ChatMessage(origin, type, content, timeStamp)); + sendingPartner.AddMessage(new ChatMessage(origin, type, content, timeStamp, true)); break; } }); @@ -178,8 +177,9 @@ namespace PolyChat new ChatMessage( origin, item["type"].ToString(), - item["content"].ToString()//, - //DateTime.Parse(item["timestamp"].ToString()) + item["content"].ToString(), + DateTime.Parse(item["timestamp"].ToString()), + false // TODO: FIX !!!! ) ); } diff --git a/PolyChat/Models/ChatMessage.cs b/PolyChat/Models/ChatMessage.cs index 7428068..1f6d272 100644 --- a/PolyChat/Models/ChatMessage.cs +++ b/PolyChat/Models/ChatMessage.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Text.Json; +using Windows.UI.Xaml; namespace PolyChat.Models { @@ -10,38 +11,23 @@ namespace PolyChat.Models public string Type; public string Content; public DateTime TimeStamp; - public readonly bool Foreign; + public HorizontalAlignment Align; + private bool Foreign; /// - /// Create own Message (directly sent) - /// - /// My IP - /// Message Type - /// Message Content (not JSON) - public ChatMessage(string origin, string type, string content) - { - Origin = origin; - TimeStamp = DateTime.Now; - Type = type; - Content = content; - // TODO - Foreign = false; - Debug.WriteLine("Created Message: " + ToString()); - } - - /// - /// Create Message loaded with timestamp + /// Create Message /// /// Origin IP /// Message Type, usually "message" /// Message Content, usually plain text /// Parsed DateTime - public ChatMessage(string origin, string type, string content, DateTime timeStamp, bool foreign = false) + public ChatMessage(string origin, string type, string content, DateTime timeStamp, bool foreign) { Origin = origin; TimeStamp = timeStamp; Type = type; Content = content; + Align = foreign ? HorizontalAlignment.Right : HorizontalAlignment.Left; Foreign = foreign; Debug.WriteLine("Created Loaded Message: " + ToString()); } From f0d785cb13a69997b81f10df55570c0ad85ebf32 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 16:22:38 +0200 Subject: [PATCH 41/82] set encryption keys --- PolyChat/Models/FileManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PolyChat/Models/FileManager.cs b/PolyChat/Models/FileManager.cs index e841c29..d0ca688 100644 --- a/PolyChat/Models/FileManager.cs +++ b/PolyChat/Models/FileManager.cs @@ -173,7 +173,7 @@ namespace PolyChat.Models /// private String[] genKeys() { - return new String[] {"sehrSichererKey", "nochVielSichererKey"}; + return new String[] {"12345678", "12345678" }; } From 072e12d427f5c6793b0512ffa1b2ce1e9224c04e Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 16:24:41 +0200 Subject: [PATCH 42/82] ChatMessages on THEEEEEEEEEEEEEEE RIGHT --- PolyChat/Controller.cs | 2 +- PolyChat/MainPage.xaml | 13 ++++++++----- PolyChat/MainPage.xaml.cs | 10 +++++----- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index d5e6efe..afcee19 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -42,8 +42,8 @@ namespace PolyChat Serve(); // test + UIController.OnIncomingConnection("localhost"); /* - UIController.OnIncomingConnection("1.1.1.1"); UIController.OnIncomingConnection("1.2.3.4"); UIController.OnIncomingConnection("1.2.4.8"); */ diff --git a/PolyChat/MainPage.xaml b/PolyChat/MainPage.xaml index 1ed214d..550dcc3 100644 --- a/PolyChat/MainPage.xaml +++ b/PolyChat/MainPage.xaml @@ -96,13 +96,16 @@ + + + - - - - - + + + diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 2e976d2..8a4388d 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -87,16 +87,16 @@ namespace PolyChat public async void OnOpenNewChatDialog(object sender = null, RoutedEventArgs e = null) { // test - /* + /**/ OnIncomingMessage( - "1.1.1.1", + "localhost", new JObject( - new JProperty("type", "username"), - new JProperty("content", "Cloudflare") + new JProperty("type", "message"), + new JProperty("content", "Test") ).ToString(), DateTime.Now ); - */ + /**/ NewChatDialog dialog = new NewChatDialog(); var result = await SafelyOpenDialog(dialog); if (result == ContentDialogResult.Primary) From 0ef21588598cb741d48ea8227c815e18dd41b3a4 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Fri, 24 Sep 2021 08:39:48 +0200 Subject: [PATCH 43/82] remove test code, add logo svg, add screenshots --- PolyChat/Assets/polychat_logo.svg | 82 ++++++++++++++++++++++++++++++ PolyChat/Controller.cs | 2 +- PolyChat/MainPage.xaml.cs | 4 +- screenshots/polychat_dark.PNG | Bin 0 -> 21853 bytes screenshots/polychat_light.PNG | Bin 0 -> 20764 bytes 5 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 PolyChat/Assets/polychat_logo.svg create mode 100644 screenshots/polychat_dark.PNG create mode 100644 screenshots/polychat_light.PNG diff --git a/PolyChat/Assets/polychat_logo.svg b/PolyChat/Assets/polychat_logo.svg new file mode 100644 index 0000000..b4be3c6 --- /dev/null +++ b/PolyChat/Assets/polychat_logo.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 17e5e29..5056bd2 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -42,8 +42,8 @@ namespace PolyChat Serve(); // test - UIController.OnIncomingConnection("localhost"); /* + UIController.OnIncomingConnection("localhost"); UIController.OnIncomingConnection("1.2.3.4"); UIController.OnIncomingConnection("1.2.4.8"); */ diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 8a4388d..9c45126 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -87,7 +87,7 @@ namespace PolyChat public async void OnOpenNewChatDialog(object sender = null, RoutedEventArgs e = null) { // test - /**/ + /* OnIncomingMessage( "localhost", new JObject( @@ -96,7 +96,7 @@ namespace PolyChat ).ToString(), DateTime.Now ); - /**/ + */ NewChatDialog dialog = new NewChatDialog(); var result = await SafelyOpenDialog(dialog); if (result == ContentDialogResult.Primary) diff --git a/screenshots/polychat_dark.PNG b/screenshots/polychat_dark.PNG new file mode 100644 index 0000000000000000000000000000000000000000..32517331cac9e89d6a42d00de149b76cd9c06413 GIT binary patch literal 21853 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 WQxCl14FwLie6syx=12Sg{Qm%!d^e*2 literal 0 HcmV?d00001 diff --git a/screenshots/polychat_light.PNG b/screenshots/polychat_light.PNG new file mode 100644 index 0000000000000000000000000000000000000000..86c4e88f2199e472607716f6be4a61035d407ef5 GIT binary patch literal 20764 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 literal 0 HcmV?d00001 From 7135486810c2f61ca5b5069f1e5b02c85b3cc2c6 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Wed, 22 Sep 2021 13:58:55 +0200 Subject: [PATCH 44/82] add initial packet, close socket on disconnect, move server start to Controller.cs --- PolyChat/Controller.cs | 26 ++++++++++++++++-- PolyChat/Models/Connection.cs | 52 ++++++++++++++--------------------- 2 files changed, 44 insertions(+), 34 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 43f856e..992576d 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -3,6 +3,8 @@ using System.Diagnostics; using Newtonsoft.Json.Linq; using System.Net; using PolyChat.Models; +using SocketIOSharp.Server; +using SocketIOSharp.Server.Client; namespace PolyChat { @@ -31,13 +33,31 @@ namespace PolyChat public void Connect(string ip) { Debug.WriteLine("--- Controller.Connect ---"); - Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(Data))); + new Connection(ip, PORT, Data => OnMessage(Data)); } private void Serve() { - Debug.WriteLine("--- Controller.Serve ---"); - Connections.Add("unknownIP", new Connection(PORT, Data => OnMessage(Data))); + Debug.WriteLine("! SERVER STARTING !"); + SocketIOServer server = new SocketIOServer(new SocketIOServerOption( + PORT + )); + server.Start(); + Debug.WriteLine("Port " + server.Option.Port); + Debug.WriteLine("Path " + server.Option.Path); + // listen for connection + server.OnConnection((SocketIOSocket socket) => + { + Debug.WriteLine("--- Client connected! ---"); + // setup event listeners + socket.On("initial", (JToken[] data) => + { + Debug.WriteLine("--- initial packet received ---"); + string ForeignIp = data.ToString(); + //Todo deserialize inital packet and extract ip address + Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(Data))); + }); + }); } public void SendMessage(string ip, string message) diff --git a/PolyChat/Models/Connection.cs b/PolyChat/Models/Connection.cs index 32daad4..f214739 100644 --- a/PolyChat/Models/Connection.cs +++ b/PolyChat/Models/Connection.cs @@ -4,8 +4,8 @@ using EngineIOSharp.Common.Enum; using Newtonsoft.Json.Linq; using SocketIOSharp.Client; using SocketIOSharp.Common; -using SocketIOSharp.Server; using SocketIOSharp.Server.Client; +using PolyChat.Models; namespace PolyChat.Models { @@ -14,10 +14,12 @@ namespace PolyChat.Models private SocketIOClient Client; private SocketIOSocket Socket; private bool Connected = false; + private readonly string IP; public Connection(string ip, ushort port, Action onMessage) { Debug.WriteLine("! CONNECTING TO SERVER !"); + IP = ip; // establish connection Client = new SocketIOClient(new SocketIOClientOption(EngineIOScheme.http, ip, port)); Client.Connect(); @@ -28,37 +30,15 @@ namespace PolyChat.Models Client.On("message", (Action) onMessage); } - public Connection(ushort port, Action onMessage) + public Connection(SocketIOSocket socket, Action onMessage) { - Debug.WriteLine("! SERVER STARTING !"); - SocketIOServer server = new SocketIOServer(new SocketIOServerOption( - port - )); - server.Start(); - Debug.WriteLine("Port " + server.Option.Port); - Debug.WriteLine("Path " + server.Option.Path); - // listen for connection - server.OnConnection((SocketIOSocket socket) => - { - Console.WriteLine("--- Client connected! ---"); - Socket = socket; - Connected = true; - // setup event listeners - Socket.On("input", (JToken[] data) => - { - Debug.WriteLine("--- Incoming input ---"); - onMessage(data); - socket.Emit("echo", data); - }); - Socket.On("message", (JToken[] data) => - { - Debug.WriteLine("--- Incoming message ---"); - onMessage(data); - socket.Emit("echo", data); - }); - Socket.On(SocketIOEvent.DISCONNECT, OnDisconnect); - Socket.On(SocketIOEvent.ERROR, (JToken[] Data) => OnError(Data)); - }); + Socket = socket; + Socket.On(SocketIOEvent.DISCONNECT, OnDisconnect); + Socket.On(SocketIOEvent.ERROR, (JToken[] Data) => OnError(Data)); + Socket.On("message", (Action)onMessage); + + //we are connected if we got here, inital packet was already received + Connected = true; } public void SendMessage(string message) { @@ -74,6 +54,8 @@ namespace PolyChat.Models private void OnConnect() { + Debug.WriteLine("--- Sending initial packet to server ---"); + Client.Emit("initial", IP); Debug.WriteLine("--- Connection successfull ---"); Connected = true; } @@ -81,6 +63,7 @@ namespace PolyChat.Models { Debug.WriteLine("--- Disconnected! ---"); Connected = false; + Close(); } private void OnError(JToken[] data) { @@ -90,10 +73,17 @@ namespace PolyChat.Models else Debug.WriteLine("Unkown Error"); Debug.WriteLine("---"); + Close(); } // Getters + public void Close() + { + if (Client != null) Client.Close(); + if (Socket != null) Socket.Close(); + } + public bool IsConnected() { return Connected; From 0f305a216e141e6d0bd8ef31f943bc8e5d6db587 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Wed, 22 Sep 2021 14:57:36 +0200 Subject: [PATCH 45/82] SendMessage is now Json --- PolyChat/Controller.cs | 11 ++++++++--- PolyChat/MainPage.xaml.cs | 17 +++++++++++------ PolyChat/Models/Connection.cs | 2 +- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 992576d..c65e1d5 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -50,20 +50,24 @@ namespace PolyChat { Debug.WriteLine("--- Client connected! ---"); // setup event listeners - socket.On("initial", (JToken[] data) => + socket.On("initial", async (JToken[] data) => { Debug.WriteLine("--- initial packet received ---"); string ForeignIp = data.ToString(); //Todo deserialize inital packet and extract ip address Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(Data))); + UIController.OnIncomingConnection(ForeignIp); }); }); } - public void SendMessage(string ip, string message) + public void SendMessage(string ip, string type, string content) { Debug.WriteLine("--- Controller.SendMessage ---"); - Connections[ip].SendMessage(message); + Debug.WriteLine($"{type} -> {ip} content: {content}"); + string json = $"{{ type: {type}, content: {content} }}"; + Debug.WriteLine($"json: {json}"); + Connections[ip].SendMessage(json); } private void OnMessage(JToken[] data) @@ -72,6 +76,7 @@ namespace PolyChat if (data != null && data.Length > 0 && data[0] != null) { Debug.WriteLine("Message: " + data[0]); + Debug.WriteLine($"DATA: {data[0].ToString()}"); } else Debug.WriteLine("Undefined: " + data); } diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 67f6688..8f51424 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -5,6 +5,7 @@ using System; using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; +using Windows.UI.Core; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -51,7 +52,7 @@ namespace PolyChat public void OnSendMessage(object sender = null, RoutedEventArgs e = null) { selectedPartner.AddMessage(new ChatMessage(username, "message" , inputSend.Text)); - Controller.SendMessage(selectedPartner.Code, inputSend.Text); + Controller.SendMessage(selectedPartner.Code, "message", inputSend.Text); // clear input inputSend.Text = ""; } @@ -89,12 +90,16 @@ namespace PolyChat /// Adds a new ChatPartner to the UI with default Name. /// /// IP Adress, gets shown as Util.IP > Code - public void OnIncomingConnection(string ip) + public async void OnIncomingConnection(string ip) { - Partners.Add(new ChatPartner( - "Connecting...", - ip - )); + await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + Partners.Add(new ChatPartner( + "Connecting...", + ip + )); + updateNoChatsPlaceholder(); + }); } /// /// Adds an message to the UI, based on .sender if known diff --git a/PolyChat/Models/Connection.cs b/PolyChat/Models/Connection.cs index f214739..6700330 100644 --- a/PolyChat/Models/Connection.cs +++ b/PolyChat/Models/Connection.cs @@ -35,7 +35,7 @@ namespace PolyChat.Models Socket = socket; Socket.On(SocketIOEvent.DISCONNECT, OnDisconnect); Socket.On(SocketIOEvent.ERROR, (JToken[] Data) => OnError(Data)); - Socket.On("message", (Action)onMessage); + Socket.On("message", (Action) onMessage); //we are connected if we got here, inital packet was already received Connected = true; From e6e5bee4efc0a2761df6b1e8b2651da8ba64cda1 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Wed, 22 Sep 2021 14:28:43 +0200 Subject: [PATCH 46/82] add outgoing connections to dict --- PolyChat/Controller.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index c65e1d5..6f432f3 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -33,7 +33,7 @@ namespace PolyChat public void Connect(string ip) { Debug.WriteLine("--- Controller.Connect ---"); - new Connection(ip, PORT, Data => OnMessage(Data)); + Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(Data))); } private void Serve() From 05e3e9e0c44b792d9aeaaf253788c87754852f79 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Wed, 22 Sep 2021 15:14:25 +0200 Subject: [PATCH 47/82] Added our default ip address, added ip as param to onMessage --- PolyChat/Controller.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 6f432f3..24687ae 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -28,12 +28,16 @@ namespace PolyChat UIController = uiController; OwnIP = getIP(); Serve(); + + //Connect("10.1.211.26"); // Marc + //Connect("10.1.218.90"); // Felix + //Connect("10.4.141.77"); // Pat } public void Connect(string ip) { Debug.WriteLine("--- Controller.Connect ---"); - Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(Data))); + Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(ip, Data))); } private void Serve() @@ -55,7 +59,7 @@ namespace PolyChat Debug.WriteLine("--- initial packet received ---"); string ForeignIp = data.ToString(); //Todo deserialize inital packet and extract ip address - Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(Data))); + Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data))); UIController.OnIncomingConnection(ForeignIp); }); }); @@ -70,13 +74,14 @@ namespace PolyChat Connections[ip].SendMessage(json); } - private void OnMessage(JToken[] data) + private void OnMessage(string ip, JToken[] data) { Debug.WriteLine("--- Controller.OnMessage ---"); if (data != null && data.Length > 0 && data[0] != null) { Debug.WriteLine("Message: " + data[0]); - Debug.WriteLine($"DATA: {data[0].ToString()}"); + Debug.WriteLine($"RAW: {data[0].ToString()}"); + UIController.OnIncomingMessage(ip, data[0].ToString()); } else Debug.WriteLine("Undefined: " + data); } From b42a2f9728fd713f099419d8004219c6d49c4422 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Wed, 22 Sep 2021 15:50:19 +0200 Subject: [PATCH 48/82] Fixed json formatting in SendMessage --- PolyChat/Controller.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 24687ae..1f202e3 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -42,7 +42,7 @@ namespace PolyChat private void Serve() { - Debug.WriteLine("! SERVER STARTING !"); + Debug.WriteLine("--- Controller.Serve ---"); SocketIOServer server = new SocketIOServer(new SocketIOServerOption( PORT )); @@ -69,9 +69,12 @@ namespace PolyChat { Debug.WriteLine("--- Controller.SendMessage ---"); Debug.WriteLine($"{type} -> {ip} content: {content}"); - string json = $"{{ type: {type}, content: {content} }}"; - Debug.WriteLine($"json: {json}"); - Connections[ip].SendMessage(json); + JObject json = new JObject( + new JProperty("type", type), + new JProperty("content", content) + ); + Debug.WriteLine($"json: {json.ToString()}"); + Connections[ip].SendMessage(json.ToString()); } private void OnMessage(string ip, JToken[] data) From 6f2c442e94b4fdac9dbe3269b9c95ff4bf3e2866 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Wed, 22 Sep 2021 16:21:40 +0200 Subject: [PATCH 49/82] small fix for correct ip display --- PolyChat/Controller.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 1f202e3..099fada 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -57,7 +57,7 @@ namespace PolyChat socket.On("initial", async (JToken[] data) => { Debug.WriteLine("--- initial packet received ---"); - string ForeignIp = data.ToString(); + string ForeignIp = data[0].ToString(); //Todo deserialize inital packet and extract ip address Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data))); UIController.OnIncomingConnection(ForeignIp); From e2de9f5917aae298b045e10ab54fa1afc5ce2924 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Wed, 22 Sep 2021 15:51:53 +0200 Subject: [PATCH 50/82] add retry logic on disconnect, silently delete chat if disconnected after connect was succesfull --- PolyChat/Controller.cs | 13 +++++++++++-- PolyChat/MainPage.xaml.cs | 33 ++++++++++++++++++++++++++------- PolyChat/Models/Connection.cs | 12 ++++++++---- 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 099fada..8a48a6d 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -37,7 +37,7 @@ namespace PolyChat public void Connect(string ip) { Debug.WriteLine("--- Controller.Connect ---"); - Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(ip, Data))); + Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(ip, Data), CloseChat)); } private void Serve() @@ -59,7 +59,7 @@ namespace PolyChat Debug.WriteLine("--- initial packet received ---"); string ForeignIp = data[0].ToString(); //Todo deserialize inital packet and extract ip address - Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data))); + Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data), CloseChat)); UIController.OnIncomingConnection(ForeignIp); }); }); @@ -89,6 +89,15 @@ namespace PolyChat else Debug.WriteLine("Undefined: " + data); } + public void CloseChat(string IP, bool wasConnected = true) + { + Connections[IP].Close(); + Connections.Remove(IP); + UIController.OnChatPartnerDeleted(IP); + if(!wasConnected) + UIController.ShowConnectionError(IP, $"Connection to {IP} failed..."); + } + public string getIP() { IPHostEntry ipEntry = Dns.GetHostEntry(Dns.GetHostName()); diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 8f51424..0dc0f43 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -36,15 +36,22 @@ namespace PolyChat updateSendButtonEnabled(); } - public async void ShowConnectionError(string message) + public async void ShowConnectionError(string code, string message) { - ConnectionFailedDialog dialog = new ConnectionFailedDialog(message); - var result = await dialog.ShowAsync(); - if (result == ContentDialogResult.Primary) + await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => { - //retry - } - // else abort -> del chat + ConnectionFailedDialog dialog = new ConnectionFailedDialog(message); + var result = await dialog.ShowAsync(); + if (result == ContentDialogResult.Primary) + { + Controller.Connect(code); + Partners.Add(new ChatPartner( + "Connecting...", + code + )); + updateNoChatsPlaceholder(); + } + }); } // EVENTS @@ -113,10 +120,22 @@ namespace PolyChat private void OnDeleteChat(object sender = null, RoutedEventArgs e = null) { + Controller.CloseChat(selectedPartner.Code); Partners.Remove(selectedPartner); updateNoChatsPlaceholder(); updateNoChatSelected(); } + + public async void OnChatPartnerDeleted(string code) + { + await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + Partners.Remove(Partners.First(p => p.Code == code)); + selectedPartner = null; + updateNoChatsPlaceholder(); + updateNoChatSelected(); + }); + } public void OnChatPartnerSelected(object sender, RoutedEventArgs e) { string code = ((RadioButton)sender).Tag.ToString(); diff --git a/PolyChat/Models/Connection.cs b/PolyChat/Models/Connection.cs index 6700330..971e6af 100644 --- a/PolyChat/Models/Connection.cs +++ b/PolyChat/Models/Connection.cs @@ -15,11 +15,13 @@ namespace PolyChat.Models private SocketIOSocket Socket; private bool Connected = false; private readonly string IP; + private Action DeleteConnection; - public Connection(string ip, ushort port, Action onMessage) + public Connection(string ip, ushort port, Action onMessage, Action onClose) { Debug.WriteLine("! CONNECTING TO SERVER !"); IP = ip; + DeleteConnection = onClose; // establish connection Client = new SocketIOClient(new SocketIOClientOption(EngineIOScheme.http, ip, port)); Client.Connect(); @@ -29,10 +31,11 @@ namespace PolyChat.Models Client.On(SocketIOEvent.ERROR, (JToken[] Data) => OnError(Data)); Client.On("message", (Action) onMessage); } - - public Connection(SocketIOSocket socket, Action onMessage) + + public Connection(SocketIOSocket socket, Action onMessage, Action onClose) { Socket = socket; + DeleteConnection = onClose; Socket.On(SocketIOEvent.DISCONNECT, OnDisconnect); Socket.On(SocketIOEvent.ERROR, (JToken[] Data) => OnError(Data)); Socket.On("message", (Action) onMessage); @@ -62,8 +65,9 @@ namespace PolyChat.Models private void OnDisconnect() { Debug.WriteLine("--- Disconnected! ---"); + Debug.WriteLine($"--- Deleting Connection with IP: {IP}---"); + DeleteConnection(IP, IsConnected()); Connected = false; - Close(); } private void OnError(JToken[] data) { From ea815475408f710a4e105d4734e5eff690e5f5cb Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 08:59:22 +0200 Subject: [PATCH 51/82] ConnectionFailedDialog -> Dialog for Success and Error Messages with heading, message and buttons/actions --- PolyChat/Controller.cs | 14 ++-- PolyChat/MainPage.xaml.cs | 38 ++++++---- PolyChat/Models/DialogButton.cs | 20 ++++++ PolyChat/PolyChat.csproj | 7 +- PolyChat/Views/ConnectionFailedDialog.xaml.cs | 26 ------- ...onnectionFailedDialog.xaml => Dialog.xaml} | 13 ++-- PolyChat/Views/Dialog.xaml.cs | 70 +++++++++++++++++++ 7 files changed, 132 insertions(+), 56 deletions(-) create mode 100644 PolyChat/Models/DialogButton.cs delete mode 100644 PolyChat/Views/ConnectionFailedDialog.xaml.cs rename PolyChat/Views/{ConnectionFailedDialog.xaml => Dialog.xaml} (59%) create mode 100644 PolyChat/Views/Dialog.xaml.cs diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 8a48a6d..5088c48 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -8,6 +8,10 @@ using SocketIOSharp.Server.Client; namespace PolyChat { + + // 10.1.211.26 Marc + // 10.1.218.90 Felix + // 10.4.141.77 Pat class Controller { // Constants @@ -28,10 +32,6 @@ namespace PolyChat UIController = uiController; OwnIP = getIP(); Serve(); - - //Connect("10.1.211.26"); // Marc - //Connect("10.1.218.90"); // Felix - //Connect("10.4.141.77"); // Pat } public void Connect(string ip) @@ -43,9 +43,7 @@ namespace PolyChat private void Serve() { Debug.WriteLine("--- Controller.Serve ---"); - SocketIOServer server = new SocketIOServer(new SocketIOServerOption( - PORT - )); + SocketIOServer server = new SocketIOServer(new SocketIOServerOption(PORT)); server.Start(); Debug.WriteLine("Port " + server.Option.Port); Debug.WriteLine("Path " + server.Option.Path); @@ -95,7 +93,7 @@ namespace PolyChat Connections.Remove(IP); UIController.OnChatPartnerDeleted(IP); if(!wasConnected) - UIController.ShowConnectionError(IP, $"Connection to {IP} failed..."); + UIController.ShowConnectionError("Connection close", IP, $"Connection to {IP} failed..."); } public string getIP() diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 0dc0f43..1eccd36 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -36,21 +36,31 @@ namespace PolyChat updateSendButtonEnabled(); } - public async void ShowConnectionError(string code, string message) + public async void ShowConnectionError(string code, string heading, string message) { - await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () => + await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { - ConnectionFailedDialog dialog = new ConnectionFailedDialog(message); - var result = await dialog.ShowAsync(); - if (result == ContentDialogResult.Primary) - { - Controller.Connect(code); - Partners.Add(new ChatPartner( - "Connecting...", - code - )); - updateNoChatsPlaceholder(); - } + Dialog dialog = new Dialog( + Dialog.TYPE_ERROR, + heading, + message, + new DialogButton( + "Retry", + () => + { + Controller.Connect(code); + Partners.Add(new ChatPartner( + "Connecting...", + code + )); + updateNoChatsPlaceholder(); + } + ), + new DialogButton( + "Ignore", + () => { /* do nothing */ } + ) + ); }); } @@ -58,7 +68,7 @@ namespace PolyChat public void OnSendMessage(object sender = null, RoutedEventArgs e = null) { - selectedPartner.AddMessage(new ChatMessage(username, "message" , inputSend.Text)); + selectedPartner.AddMessage(new ChatMessage(username, "message", inputSend.Text)); Controller.SendMessage(selectedPartner.Code, "message", inputSend.Text); // clear input inputSend.Text = ""; diff --git a/PolyChat/Models/DialogButton.cs b/PolyChat/Models/DialogButton.cs new file mode 100644 index 0000000..959e5c2 --- /dev/null +++ b/PolyChat/Models/DialogButton.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PolyChat.Models +{ + public class DialogButton + { + public string Text; + public Action Action; + + public DialogButton(string text, Action action) + { + Text = text; + Action = action; + } + } +} diff --git a/PolyChat/PolyChat.csproj b/PolyChat/PolyChat.csproj index 3f479cf..418a809 100644 --- a/PolyChat/PolyChat.csproj +++ b/PolyChat/PolyChat.csproj @@ -119,6 +119,7 @@ App.xaml + @@ -130,8 +131,8 @@ - - ConnectionFailedDialog.xaml + + Dialog.xaml EditUsernameDialog.xaml @@ -164,7 +165,7 @@ MSBuild:Compile Designer - + MSBuild:Compile Designer diff --git a/PolyChat/Views/ConnectionFailedDialog.xaml.cs b/PolyChat/Views/ConnectionFailedDialog.xaml.cs deleted file mode 100644 index 1c09a8f..0000000 --- a/PolyChat/Views/ConnectionFailedDialog.xaml.cs +++ /dev/null @@ -1,26 +0,0 @@ -using PolyChat.Models; -using PolyChat.Util; -using System; -using System.Collections.ObjectModel; -using System.Linq; -using Windows.UI.Popups; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Media; - -// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 - -namespace PolyChat.Views -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - public sealed partial class ConnectionFailedDialog : ContentDialog - { - public ConnectionFailedDialog(string message) - { - this.InitializeComponent(); - textError.Text = message; - } - } -} diff --git a/PolyChat/Views/ConnectionFailedDialog.xaml b/PolyChat/Views/Dialog.xaml similarity index 59% rename from PolyChat/Views/ConnectionFailedDialog.xaml rename to PolyChat/Views/Dialog.xaml index fa8916e..bc60eb8 100644 --- a/PolyChat/Views/ConnectionFailedDialog.xaml +++ b/PolyChat/Views/Dialog.xaml @@ -1,17 +1,20 @@ - - + + diff --git a/PolyChat/Views/Dialog.xaml.cs b/PolyChat/Views/Dialog.xaml.cs new file mode 100644 index 0000000..f342693 --- /dev/null +++ b/PolyChat/Views/Dialog.xaml.cs @@ -0,0 +1,70 @@ +using PolyChat.Models; +using PolyChat.Util; +using System; +using System.Collections.ObjectModel; +using System.Linq; +using Windows.Foundation; +using Windows.UI.Core; +using Windows.UI.Popups; +using Windows.UI.Xaml; +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media; + +// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 + +namespace PolyChat.Views +{ + /// + /// An empty page that can be used on its own or navigated to within a Frame. + /// + public sealed partial class Dialog : ContentDialog + { + public const string TYPE_ERROR = "error"; + public const string TYPE_SUCCESS = "success"; + private Action Primary; + private Action Secondary; + public Dialog(string type, string header, string message, DialogButton primary, DialogButton secondary) + { + this.InitializeComponent(); + Title = header; + setType(type, message); + PrimaryButtonText = primary.Text; + SecondaryButtonText = secondary.Text; + // TODO: use event handlers and asign actions here + Primary = primary.Action; + Secondary = secondary.Action; + // show + ShowDialogAsync(); + } + + private void setType(string type, string message) + { + switch (type) + { + case TYPE_ERROR: + textError.Text = message; + textError.Visibility = Visibility.Visible; + break; + case TYPE_SUCCESS: + textSuccess.Text = message; + textSuccess.Visibility = Visibility.Visible; + break; + } + } + + private async void ShowDialogAsync() + { + await ShowAsync(); + } + + private void OnPrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + Primary(); + } + + private void OnSecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) + { + Secondary(); + } + } +} From 55bd72b467c4494d1cc8c6d612e0f25722fc50ea Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 08:41:22 +0200 Subject: [PATCH 52/82] retry working, radiobuttons now triggering on every click, deleteChat Button working --- PolyChat/MainPage.xaml | 2 +- PolyChat/MainPage.xaml.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/PolyChat/MainPage.xaml b/PolyChat/MainPage.xaml index ebdfd13..e2d66c4 100644 --- a/PolyChat/MainPage.xaml +++ b/PolyChat/MainPage.xaml @@ -53,7 +53,7 @@ - + diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 1eccd36..08b3b2e 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -124,7 +124,7 @@ namespace PolyChat /// ChatMessage public void OnIncomingMessage(string origin, string json) { - ChatPartner sendingPartner = Partners.First(p => p.Code == origin); + ChatPartner sendingPartner = Partners.FirstOrDefault(p => p.Code == origin); sendingPartner.AddMessage(new ChatMessage(origin, json)); } @@ -140,7 +140,7 @@ namespace PolyChat { await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { - Partners.Remove(Partners.First(p => p.Code == code)); + Partners.Remove(Partners.FirstOrDefault(p => p.Code.Equals(code))); selectedPartner = null; updateNoChatsPlaceholder(); updateNoChatSelected(); @@ -149,7 +149,7 @@ namespace PolyChat public void OnChatPartnerSelected(object sender, RoutedEventArgs e) { string code = ((RadioButton)sender).Tag.ToString(); - selectedPartner = Partners.First(p => p.Code == code); + selectedPartner = Partners.FirstOrDefault(p => p.Code == code); listViewMessages.ItemsSource = selectedPartner.Messages; selectedPartnerName.Text = selectedPartner.Name; updateNoChatSelected(); From 92bc795aff67cd8ddde5f8b0a932ad09faa160a4 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 10:21:55 +0200 Subject: [PATCH 53/82] Added OnIncomingMessages (for loading messages from json array) --- PolyChat/Controller.cs | 7 +++---- PolyChat/MainPage.xaml.cs | 38 +++++++++++++++++++++++++++------- PolyChat/Models/ChatMessage.cs | 30 ++++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 12 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 5088c48..5645822 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -80,8 +80,7 @@ namespace PolyChat Debug.WriteLine("--- Controller.OnMessage ---"); if (data != null && data.Length > 0 && data[0] != null) { - Debug.WriteLine("Message: " + data[0]); - Debug.WriteLine($"RAW: {data[0].ToString()}"); + Debug.WriteLine("RAW: " + data[0]); UIController.OnIncomingMessage(ip, data[0].ToString()); } else Debug.WriteLine("Undefined: " + data); @@ -92,8 +91,8 @@ namespace PolyChat Connections[IP].Close(); Connections.Remove(IP); UIController.OnChatPartnerDeleted(IP); - if(!wasConnected) - UIController.ShowConnectionError("Connection close", IP, $"Connection to {IP} failed..."); + string heading = wasConnected ? "Connection Closed" : "Connection Failed"; + UIController.ShowConnectionError(IP, heading, $"Connecting to {IP} failed..."); } public string getIP() diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 08b3b2e..cfa30b5 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -1,8 +1,10 @@ -using PolyChat.Models; +using Newtonsoft.Json.Linq; +using PolyChat.Models; using PolyChat.Util; using PolyChat.Views; using System; using System.Collections.ObjectModel; +using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using Windows.UI.Core; @@ -36,7 +38,7 @@ namespace PolyChat updateSendButtonEnabled(); } - public async void ShowConnectionError(string code, string heading, string message) + public async void ShowConnectionError(string param, string heading, string message) { await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { @@ -48,10 +50,10 @@ namespace PolyChat "Retry", () => { - Controller.Connect(code); + Controller.Connect(param); Partners.Add(new ChatPartner( "Connecting...", - code + param )); updateNoChatsPlaceholder(); } @@ -122,10 +124,32 @@ namespace PolyChat /// Adds an message to the UI, based on .sender if known /// /// ChatMessage - public void OnIncomingMessage(string origin, string json) + public async void OnIncomingMessage(string origin, string json) { - ChatPartner sendingPartner = Partners.FirstOrDefault(p => p.Code == origin); - sendingPartner.AddMessage(new ChatMessage(origin, json)); + await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + ChatPartner sendingPartner = Partners.FirstOrDefault(p => p.Code == origin); + sendingPartner.AddMessage(new ChatMessage(origin, json)); + }); + } + public async void OnIncomingMessages(string origin, string json) + { + await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + ChatPartner sendingPartner = Partners.FirstOrDefault(p => p.Code == origin); + JArray arr = JArray.Parse(json); + foreach (JObject item in arr) + { + sendingPartner.AddMessage( + new ChatMessage( + origin, + item["type"].ToString(), + item["content"].ToString()//, + //DateTime.Parse(item["timestamp"].ToString()) + ) + ); + } + }); } private void OnDeleteChat(object sender = null, RoutedEventArgs e = null) diff --git a/PolyChat/Models/ChatMessage.cs b/PolyChat/Models/ChatMessage.cs index 30b3ea2..9fd22c2 100644 --- a/PolyChat/Models/ChatMessage.cs +++ b/PolyChat/Models/ChatMessage.cs @@ -11,7 +11,13 @@ namespace PolyChat.Models public string Content; public DateTime TimeStamp; public readonly bool Foreign; - // + + /// + /// Create own Message (directly sent) + /// + /// My IP + /// Message Type + /// Message Content (not JSON) public ChatMessage(string origin, string type, string content) { Origin = origin; @@ -23,6 +29,28 @@ namespace PolyChat.Models Debug.WriteLine("Created Message: " + ToString()); } + /// + /// Create Message loaded with timestamp + /// + /// Origin IP + /// Message Type + /// Message Content (not JSON) + /// Message Content (not JSON) + public ChatMessage(string origin, string type, string content, DateTime timeStamp, bool foreign = false) + { + Origin = origin; + TimeStamp = timeStamp; + Type = type; + Content = content; + Foreign = foreign; + Debug.WriteLine("Created Loaded Message: " + ToString()); + } + + /// + /// Create foreign Message (directly incoming) + /// + /// Foreign IP + /// Message Content as JSON with type and content public ChatMessage(string origin, string json) { Origin = origin; From e1ea49bf716d619ceadcde9904ccd4ff63248ec3 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 09:12:03 +0200 Subject: [PATCH 54/82] stop connection if we are already connected --- PolyChat/Controller.cs | 37 ++++++++++++++++++++++++++++++----- PolyChat/Models/Connection.cs | 5 +++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 5645822..08762db 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -37,7 +37,14 @@ namespace PolyChat public void Connect(string ip) { Debug.WriteLine("--- Controller.Connect ---"); - Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(ip, Data), CloseChat)); + if (isInConnections(ip)) + { + Debug.WriteLine("---- We have an active connection to this client. ABORT! ----"); + CloseChatUI(ip); + //Todo show error! + } + else + Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(ip, Data), CloseChat)); } private void Serve() @@ -56,9 +63,16 @@ namespace PolyChat { Debug.WriteLine("--- initial packet received ---"); string ForeignIp = data[0].ToString(); - //Todo deserialize inital packet and extract ip address - Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data), CloseChat)); - UIController.OnIncomingConnection(ForeignIp); + if (isInConnections(ForeignIp)) + { + Debug.WriteLine("---- We have an active connection to this client. ABORT! ----");//Todo show error! + CloseChatUI(ForeignIp); + } + else + { + Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data), CloseChat)); + UIController.OnIncomingConnection(ForeignIp); + } }); }); } @@ -90,9 +104,22 @@ namespace PolyChat { Connections[IP].Close(); Connections.Remove(IP); + CloseChatUI(IP,wasConnected); + } + + private void CloseChatUI(string IP, bool wasConnected = true) + { UIController.OnChatPartnerDeleted(IP); string heading = wasConnected ? "Connection Closed" : "Connection Failed"; - UIController.ShowConnectionError(IP, heading, $"Connecting to {IP} failed..."); + if (!wasConnected) + UIController.ShowConnectionError(IP, heading, $"Connecting to {IP} failed..."); + } + + private bool isInConnections(string IP) + { + if(Connections.ContainsKey(IP)) + return true; + return false; } public string getIP() diff --git a/PolyChat/Models/Connection.cs b/PolyChat/Models/Connection.cs index 971e6af..b1d8600 100644 --- a/PolyChat/Models/Connection.cs +++ b/PolyChat/Models/Connection.cs @@ -92,5 +92,10 @@ namespace PolyChat.Models { return Connected; } + + public string getIP() + { + return IP; + } } } From 1b3d1336571bd9f88d130216df10e065cc96aa1f Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 09:41:56 +0200 Subject: [PATCH 55/82] allow deletion of chats which are not started by me --- PolyChat/Controller.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 08762db..0dc6368 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -102,8 +102,12 @@ namespace PolyChat public void CloseChat(string IP, bool wasConnected = true) { - Connections[IP].Close(); - Connections.Remove(IP); + Debug.WriteLine($"Deleting connection with IP:{IP}"); + if (IP != null && Connections.ContainsKey(IP)) + { + Connections[IP].Close(); + Connections.Remove(IP); + } CloseChatUI(IP,wasConnected); } From fb098db63d2083f480fbe0820d89e8dd40dcdcfd Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 09:52:54 +0200 Subject: [PATCH 56/82] fix ip in intial packet (send own ip, not foreign) --- PolyChat/Controller.cs | 3 ++- PolyChat/Models/Connection.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 0dc6368..4d3959a 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -63,6 +63,7 @@ namespace PolyChat { Debug.WriteLine("--- initial packet received ---"); string ForeignIp = data[0].ToString(); + Debug.WriteLine($"--- this ip was in the inital packet: {ForeignIp} ---"); if (isInConnections(ForeignIp)) { Debug.WriteLine("---- We have an active connection to this client. ABORT! ----");//Todo show error! @@ -126,7 +127,7 @@ namespace PolyChat return false; } - public string getIP() + public static string getIP() { IPHostEntry ipEntry = Dns.GetHostEntry(Dns.GetHostName()); IPAddress[] addrList = ipEntry.AddressList; diff --git a/PolyChat/Models/Connection.cs b/PolyChat/Models/Connection.cs index b1d8600..9c985b6 100644 --- a/PolyChat/Models/Connection.cs +++ b/PolyChat/Models/Connection.cs @@ -58,7 +58,7 @@ namespace PolyChat.Models private void OnConnect() { Debug.WriteLine("--- Sending initial packet to server ---"); - Client.Emit("initial", IP); + Client.Emit("initial", Controller.getIP()); Debug.WriteLine("--- Connection successfull ---"); Connected = true; } From 7e5883d761b8cd9024a3a47d2583932d49559918 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 10:56:10 +0200 Subject: [PATCH 57/82] Added BoardcastMessage, Username Changed event via Broadcast, remove json parsing from ChatMessage --- PolyChat/Controller.cs | 13 +++++++++++-- PolyChat/MainPage.xaml.cs | 20 ++++++++++++++++---- PolyChat/Models/ChatMessage.cs | 20 +------------------- PolyChat/Models/ChatPartner.cs | 5 +++++ 4 files changed, 33 insertions(+), 25 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 4d3959a..2792c66 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -78,6 +78,15 @@ namespace PolyChat }); } + public void SendBroadcastMessage(string type, string content) + { + Debug.WriteLine("--- Controller.Broadcast ---"); + foreach (KeyValuePair entry in Connections) + { + SendMessage(entry.Key, type, content); + } + } + public void SendMessage(string ip, string type, string content) { Debug.WriteLine("--- Controller.SendMessage ---"); @@ -109,7 +118,7 @@ namespace PolyChat Connections[IP].Close(); Connections.Remove(IP); } - CloseChatUI(IP,wasConnected); + CloseChatUI(IP, wasConnected); } private void CloseChatUI(string IP, bool wasConnected = true) @@ -122,7 +131,7 @@ namespace PolyChat private bool isInConnections(string IP) { - if(Connections.ContainsKey(IP)) + if (Connections.ContainsKey(IP)) return true; return false; } diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index cfa30b5..9f1fded 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -6,6 +6,7 @@ using System; using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; +using System.Text.Json; using System.Threading.Tasks; using Windows.UI.Core; using Windows.UI.Xaml; @@ -99,8 +100,8 @@ namespace PolyChat if (result == ContentDialogResult.Primary) { username = dialog.getValue(); - if (username.Length == 0) textUsername.Text = "Unknown"; - else textUsername.Text = username; + textUsername.Text = username; + Controller.SendBroadcastMessage("username", username); } updateNoUsernamePlaceholder(); } @@ -128,8 +129,19 @@ namespace PolyChat { await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { + var doc = JsonDocument.Parse(json).RootElement; + string type = doc.GetProperty("type").GetString(); + string content = doc.GetProperty("content").GetString(); ChatPartner sendingPartner = Partners.FirstOrDefault(p => p.Code == origin); - sendingPartner.AddMessage(new ChatMessage(origin, json)); + switch (type) + { + case "username": + sendingPartner.SetName(Name); + break; + default: + sendingPartner.AddMessage(new ChatMessage(origin, type, content)); + break; + } }); } public async void OnIncomingMessages(string origin, string json) @@ -145,7 +157,7 @@ namespace PolyChat origin, item["type"].ToString(), item["content"].ToString()//, - //DateTime.Parse(item["timestamp"].ToString()) + //DateTime.Parse(item["timestamp"].ToString()) ) ); } diff --git a/PolyChat/Models/ChatMessage.cs b/PolyChat/Models/ChatMessage.cs index 9fd22c2..5757702 100644 --- a/PolyChat/Models/ChatMessage.cs +++ b/PolyChat/Models/ChatMessage.cs @@ -24,7 +24,7 @@ namespace PolyChat.Models TimeStamp = DateTime.Now; Type = type; Content = content; - // no json = my messages + // TODO Foreign = false; Debug.WriteLine("Created Message: " + ToString()); } @@ -46,24 +46,6 @@ namespace PolyChat.Models Debug.WriteLine("Created Loaded Message: " + ToString()); } - /// - /// Create foreign Message (directly incoming) - /// - /// Foreign IP - /// Message Content as JSON with type and content - public ChatMessage(string origin, string json) - { - Origin = origin; - // parse and save to object - var obj = JsonDocument.Parse(json).RootElement; - Type = obj.GetProperty("type").GetString(); - Content = obj.GetProperty("content").GetString(); - TimeStamp = DateTime.Now; - // json = foreign - Foreign = true; - Debug.WriteLine("Created Message: " + ToString()); - } - override public string ToString() { diff --git a/PolyChat/Models/ChatPartner.cs b/PolyChat/Models/ChatPartner.cs index 020433d..118b7af 100644 --- a/PolyChat/Models/ChatPartner.cs +++ b/PolyChat/Models/ChatPartner.cs @@ -24,5 +24,10 @@ namespace PolyChat.Models { Messages.Add(message); } + + public void SetName(string name) + { + Name = name; + } } } \ No newline at end of file From eeea23839887fb5f00ee34b14b50c715e0222f78 Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 10:50:40 +0200 Subject: [PATCH 58/82] Saving and loading but still has error(Pls fix) --- PolyChat/Controller.cs | 94 ++++++++++++++++++++++++++++++++++++++- PolyChat/MainPage.xaml.cs | 1 + 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 2792c66..8349009 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -5,6 +5,9 @@ using System.Net; using PolyChat.Models; using SocketIOSharp.Server; using SocketIOSharp.Server.Client; +using System.IO; +using System.Threading; +using System; namespace PolyChat { @@ -31,6 +34,8 @@ namespace PolyChat { UIController = uiController; OwnIP = getIP(); + loadChats(); + //SaveChats("10", "{das ist ein test}"); Serve(); } @@ -93,10 +98,12 @@ namespace PolyChat Debug.WriteLine($"{type} -> {ip} content: {content}"); JObject json = new JObject( new JProperty("type", type), - new JProperty("content", content) + new JProperty("content", content), + new JProperty("timestamp", DateTime.Now.ToString()) ); Debug.WriteLine($"json: {json.ToString()}"); Connections[ip].SendMessage(json.ToString()); + SaveChats(ip, json.ToString()); } private void OnMessage(string ip, JToken[] data) @@ -106,6 +113,7 @@ namespace PolyChat { Debug.WriteLine("RAW: " + data[0]); UIController.OnIncomingMessage(ip, data[0].ToString()); + SaveChats(ip, data[0].ToString()); } else Debug.WriteLine("Undefined: " + data); } @@ -149,5 +157,89 @@ namespace PolyChat } return null; } + + /// + /// sends chatlogs as json array to uiController wit corrosponding ip + /// + /// + public void loadChats() + { + //load dir and create if non existant + if (Directory.Exists("U:\\PolyChat\\Saves")) + { + Debug.WriteLine("--Path exists.--"); + } + else + { + Directory.CreateDirectory("U:\\PolyChat\\Saves"); + Debug.WriteLine("--Path Created--."); + } + + //go through all files and send ip and json array to ui + String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); + if (filepaths.Length > 0) + { + foreach (String path in filepaths) + { + Debug.WriteLine("---Loading Saves---"); + Debug.WriteLine("--" + path + "--"); + String jsonArr = File.ReadAllText(path); + String ip = Path.GetFileName(path); + ip = ip.Substring(0, ip.Length - 4); + Debug.WriteLine("-" + ip + "-"); + Debug.WriteLine(jsonArr); + UIController.OnIncomingMessages(ip, jsonArr); + } + } + + } + + /// + /// Saves incoming chat message to + /// + /// + /// + public void SaveChats(String ip, String json) + { + //Vielleicht noch so machen dass die mit gleicher ip nacheinander gemacht + //werden damit es ncith zu überschreibungen kommt vielleicth auch ganz oben oder am ende ne + //writing flag setzen oder auch in der datei selbst ne flag setzen + //also save fils from myself + new Thread(() => + { + if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) + { + Debug.WriteLine("--File allready exists--"); + //check for integraty of file + string output = File.ReadAllText($"U:\\PolyChat\\Saves\\{ip}.txt"); + Debug.WriteLine($"---{output}---"); + if (output.Substring(0, 1).Equals("[") && output.Substring(output.Length - 1, 1).Equals("]")) + { + Debug.WriteLine("--adding new chatmessage--"); + //structure intact + //save new chat + String saved = output.Substring(0, output.Length - 1); + output = saved + ", " + json + " ]"; + File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", output); + } + else + { + Debug.WriteLine("--Structure not intact--"); + Debug.WriteLine("--redoing file--"); + //structure not intact + //redo file + File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", $"[ {json} ]"); + } + } + else + { + Debug.WriteLine("--Creating new File--"); + //setup file + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", $"[ {json} ]"); + } + }).Start(); + } } } diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 9f1fded..7cc022e 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -164,6 +164,7 @@ namespace PolyChat }); } + private void OnDeleteChat(object sender = null, RoutedEventArgs e = null) { Controller.CloseChat(selectedPartner.Code); From a953d34122b351099b2c157ec564fef5afe6aad8 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 11:30:09 +0200 Subject: [PATCH 59/82] Removed timeStamp from SendMessage, added param Timestamp to savechat() --- PolyChat/Controller.cs | 17 ++++++++++------- PolyChat/MainPage.xaml.cs | 8 ++++---- PolyChat/Models/ChatMessage.cs | 6 +++--- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 8349009..8f5f6bc 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -98,12 +98,13 @@ namespace PolyChat Debug.WriteLine($"{type} -> {ip} content: {content}"); JObject json = new JObject( new JProperty("type", type), - new JProperty("content", content), - new JProperty("timestamp", DateTime.Now.ToString()) + new JProperty("content", content) ); Debug.WriteLine($"json: {json.ToString()}"); + // send as json Connections[ip].SendMessage(json.ToString()); - SaveChats(ip, json.ToString()); + // save to logs + SaveChats(ip, json.ToString(), DateTime.Now); } private void OnMessage(string ip, JToken[] data) @@ -111,9 +112,10 @@ namespace PolyChat Debug.WriteLine("--- Controller.OnMessage ---"); if (data != null && data.Length > 0 && data[0] != null) { + DateTime now = DateTime.Now; Debug.WriteLine("RAW: " + data[0]); - UIController.OnIncomingMessage(ip, data[0].ToString()); - SaveChats(ip, data[0].ToString()); + UIController.OnIncomingMessage(ip, data[0].ToString(), now); + SaveChats(ip, data[0].ToString(), now); } else Debug.WriteLine("Undefined: " + data); } @@ -199,10 +201,10 @@ namespace PolyChat /// /// /// - public void SaveChats(String ip, String json) + public void SaveChats(string ip, string json, DateTime timeStamp) { //Vielleicht noch so machen dass die mit gleicher ip nacheinander gemacht - //werden damit es ncith zu überschreibungen kommt vielleicth auch ganz oben oder am ende ne + //werden damit es nicht zu überschreibungen kommt vielleicth auch ganz oben oder am ende ne //writing flag setzen oder auch in der datei selbst ne flag setzen //also save fils from myself new Thread(() => @@ -217,6 +219,7 @@ namespace PolyChat { Debug.WriteLine("--adding new chatmessage--"); //structure intact + JObject obj = JObject.Parse(json); //save new chat String saved = output.Substring(0, output.Length - 1); output = saved + ", " + json + " ]"; diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 7cc022e..c3c3b84 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -125,7 +125,7 @@ namespace PolyChat /// Adds an message to the UI, based on .sender if known /// /// ChatMessage - public async void OnIncomingMessage(string origin, string json) + public async void OnIncomingMessage(string origin, string json, DateTime timeStamp) { await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { @@ -139,7 +139,7 @@ namespace PolyChat sendingPartner.SetName(Name); break; default: - sendingPartner.AddMessage(new ChatMessage(origin, type, content)); + sendingPartner.AddMessage(new ChatMessage(origin, type, content, timeStamp)); break; } }); @@ -156,8 +156,8 @@ namespace PolyChat new ChatMessage( origin, item["type"].ToString(), - item["content"].ToString()//, - //DateTime.Parse(item["timestamp"].ToString()) + item["content"].ToString(), + DateTime.Parse(item["timestamp"].ToString()) ) ); } diff --git a/PolyChat/Models/ChatMessage.cs b/PolyChat/Models/ChatMessage.cs index 5757702..32e6f49 100644 --- a/PolyChat/Models/ChatMessage.cs +++ b/PolyChat/Models/ChatMessage.cs @@ -33,9 +33,9 @@ namespace PolyChat.Models /// Create Message loaded with timestamp /// /// Origin IP - /// Message Type - /// Message Content (not JSON) - /// Message Content (not JSON) + /// Message Type, usually "message" + /// Message Content, usually plain text + /// Parsed DateTime public ChatMessage(string origin, string type, string content, DateTime timeStamp, bool foreign = false) { Origin = origin; From e27141e0136404d715972be65bd2f76930b2a6d2 Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 11:09:43 +0200 Subject: [PATCH 60/82] fix connection establishment when loading files --- PolyChat/Controller.cs | 3 +++ PolyChat/Models/ChatMessage.cs | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 8f5f6bc..4e98d70 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -166,6 +166,8 @@ namespace PolyChat /// public void loadChats() { + + //load dir and create if non existant if (Directory.Exists("U:\\PolyChat\\Saves")) { @@ -190,6 +192,7 @@ namespace PolyChat ip = ip.Substring(0, ip.Length - 4); Debug.WriteLine("-" + ip + "-"); Debug.WriteLine(jsonArr); + Connect(ip); UIController.OnIncomingMessages(ip, jsonArr); } } diff --git a/PolyChat/Models/ChatMessage.cs b/PolyChat/Models/ChatMessage.cs index 32e6f49..4056559 100644 --- a/PolyChat/Models/ChatMessage.cs +++ b/PolyChat/Models/ChatMessage.cs @@ -12,6 +12,11 @@ namespace PolyChat.Models public DateTime TimeStamp; public readonly bool Foreign; + + public ChatMessage() + { + + } /// /// Create own Message (directly sent) /// @@ -43,7 +48,7 @@ namespace PolyChat.Models Type = type; Content = content; Foreign = foreign; - Debug.WriteLine("Created Loaded Message: " + ToString()); + Debug.WriteLine("Created Loaded Message: " + ToString()); } override From b12d3f3f4f82c7403e53910e738c17bcc814b9c3 Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 11:29:54 +0200 Subject: [PATCH 61/82] quick --- PolyChat/Controller.cs | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 4e98d70..4be7c0f 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -8,6 +8,8 @@ using SocketIOSharp.Server.Client; using System.IO; using System.Threading; using System; +using System.Text; +using System.Security.Cryptography; namespace PolyChat { @@ -49,7 +51,10 @@ namespace PolyChat //Todo show error! } else + { Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(ip, Data), CloseChat)); + } + } private void Serve() @@ -166,8 +171,6 @@ namespace PolyChat /// public void loadChats() { - - //load dir and create if non existant if (Directory.Exists("U:\\PolyChat\\Saves")) { @@ -183,16 +186,17 @@ namespace PolyChat String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); if (filepaths.Length > 0) { + Debug.WriteLine("---Loading Saves"); foreach (String path in filepaths) { - Debug.WriteLine("---Loading Saves---"); - Debug.WriteLine("--" + path + "--"); + Debug.WriteLine($"--{path}"); String jsonArr = File.ReadAllText(path); String ip = Path.GetFileName(path); ip = ip.Substring(0, ip.Length - 4); - Debug.WriteLine("-" + ip + "-"); + Debug.WriteLine($"-{ip}"); Debug.WriteLine(jsonArr); Connect(ip); + UIController.OnIncomingConnection(ip); UIController.OnIncomingMessages(ip, jsonArr); } } @@ -209,7 +213,6 @@ namespace PolyChat //Vielleicht noch so machen dass die mit gleicher ip nacheinander gemacht //werden damit es nicht zu überschreibungen kommt vielleicth auch ganz oben oder am ende ne //writing flag setzen oder auch in der datei selbst ne flag setzen - //also save fils from myself new Thread(() => { if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) @@ -247,5 +250,18 @@ namespace PolyChat } }).Start(); } + + private void encode(string json) + { + byte[] plaintext = Encoding.UTF8.GetBytes(json); + byte[] entropy = new byte[20]; + using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider()) + { + rng.GetBytes(entropy); + } + + /*byte[] ciphertext = ProtectedData.Protect(plaintext, entropy, + DataProtectionScope.CurrentUser);*/ + } } } From c00a7ff379ca9d5603771a2ed6cf2acb308ca758 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 12:53:57 +0200 Subject: [PATCH 62/82] Chats now only try to connect, if they are opened --- PolyChat/Controller.cs | 9 +++++---- PolyChat/MainPage.xaml.cs | 7 +++++-- PolyChat/Models/ChatPartner.cs | 11 ++++++++++- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 4be7c0f..e168b84 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -146,9 +146,11 @@ namespace PolyChat private bool isInConnections(string IP) { - if (Connections.ContainsKey(IP)) - return true; - return false; + return Connections.ContainsKey(IP); + } + public bool IsConnected(string ip) + { + return Connections.ContainsKey(ip) && Connections[ip].IsConnected(); } public static string getIP() @@ -195,7 +197,6 @@ namespace PolyChat ip = ip.Substring(0, ip.Length - 4); Debug.WriteLine($"-{ip}"); Debug.WriteLine(jsonArr); - Connect(ip); UIController.OnIncomingConnection(ip); UIController.OnIncomingMessages(ip, jsonArr); } diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index c3c3b84..d39e27d 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Windows.UI.Core; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Data; // The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 @@ -136,6 +137,7 @@ namespace PolyChat switch (type) { case "username": + Debug.WriteLine($"! username change for {sendingPartner.Code} -> {content}"); sendingPartner.SetName(Name); break; default: @@ -156,8 +158,8 @@ namespace PolyChat new ChatMessage( origin, item["type"].ToString(), - item["content"].ToString(), - DateTime.Parse(item["timestamp"].ToString()) + item["content"].ToString()//, + //DateTime.Parse(item["timestamp"].ToString()) ) ); } @@ -190,6 +192,7 @@ namespace PolyChat listViewMessages.ItemsSource = selectedPartner.Messages; selectedPartnerName.Text = selectedPartner.Name; updateNoChatSelected(); + if (!Controller.IsConnected(code)) Controller.Connect(code); } private void OnKeyUp(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e) diff --git a/PolyChat/Models/ChatPartner.cs b/PolyChat/Models/ChatPartner.cs index 118b7af..32df3be 100644 --- a/PolyChat/Models/ChatPartner.cs +++ b/PolyChat/Models/ChatPartner.cs @@ -1,14 +1,17 @@ using SocketIOSharp.Server.Client; using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Runtime.CompilerServices; namespace PolyChat.Models { - public class ChatPartner + public class ChatPartner : INotifyPropertyChanged { public string Name; public string Code; public ObservableCollection Messages; private SocketIOSocket socketIOSocket; + public event PropertyChangedEventHandler PropertyChanged; public ChatPartner(string name, string code, ObservableCollection messages = null) { @@ -20,6 +23,11 @@ namespace PolyChat.Models public SocketIOSocket SocketIOSocket { get => socketIOSocket; set => socketIOSocket = value; } + private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + public void AddMessage(ChatMessage message) { Messages.Add(message); @@ -28,6 +36,7 @@ namespace PolyChat.Models public void SetName(string name) { Name = name; + NotifyPropertyChanged("Name"); } } } \ No newline at end of file From 7286b211bf88df4462627c48d4fd4d6682330559 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 12:40:57 +0200 Subject: [PATCH 63/82] tested build and deploy via apppackage, fixed typo in IP.cs --- PolyChat/MainPage.xaml.cs | 2 +- PolyChat/Package.appxmanifest | 4 ++-- PolyChat/PolyChat.csproj | 13 ++++++++++++- PolyChat/Util/IP.cs | 2 +- PolyChat/Views/NewChatDialog.xaml.cs | 2 +- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index d39e27d..06a9db4 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -84,7 +84,7 @@ namespace PolyChat var result = await dialog.ShowAsync(); if (result == ContentDialogResult.Primary) { - string ip = IP.GetIPfromCode(dialog.getValue()); + string ip = IP.GetIPFromCode(dialog.getValue()); Controller.Connect(ip); Partners.Add(new ChatPartner( "Connecting...", diff --git a/PolyChat/Package.appxmanifest b/PolyChat/Package.appxmanifest index 06228f6..30ac9c8 100644 --- a/PolyChat/Package.appxmanifest +++ b/PolyChat/Package.appxmanifest @@ -9,8 +9,8 @@ + Publisher="CN=PatrickMarcFelix" + Version="0.1.0.0" /> diff --git a/PolyChat/PolyChat.csproj b/PolyChat/PolyChat.csproj index 418a809..12041a4 100644 --- a/PolyChat/PolyChat.csproj +++ b/PolyChat/PolyChat.csproj @@ -17,7 +17,15 @@ 512 {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} true - false + False + False + False + True + Always + x86 + 0 + 6B0D12FC4E83C6F6997C60706A04799ED44B7E56 + SHA256 true @@ -195,6 +203,9 @@ 5.0.2 + + + 14.0 diff --git a/PolyChat/Util/IP.cs b/PolyChat/Util/IP.cs index fcb6fd6..975dcd1 100644 --- a/PolyChat/Util/IP.cs +++ b/PolyChat/Util/IP.cs @@ -7,7 +7,7 @@ namespace PolyChat.Util { private const string REGEX_IP = @"^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$"; - public static string GetIPfromCode(string code) + public static string GetIPFromCode(string code) { return code; } diff --git a/PolyChat/Views/NewChatDialog.xaml.cs b/PolyChat/Views/NewChatDialog.xaml.cs index 600c62f..4c888ba 100644 --- a/PolyChat/Views/NewChatDialog.xaml.cs +++ b/PolyChat/Views/NewChatDialog.xaml.cs @@ -29,7 +29,7 @@ namespace PolyChat.Views private void OnKeyUp(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e) { - if (!IP.ValidateIP(IP.GetIPfromCode(input.Text))) + if (!IP.ValidateIP(IP.GetIPFromCode(input.Text))) { textSuccess.Visibility = Visibility.Collapsed; textError.Visibility = Visibility.Visible; From 43d8b2919c669b9fbf908569e95c8ff1d1848650 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 14:02:42 +0200 Subject: [PATCH 64/82] Username change now dirty reactive in ui --- PolyChat/Controller.cs | 9 +++++++-- PolyChat/MainPage.xaml.cs | 16 ++++++++++++++-- PolyChat/Models/ChatPartner.cs | 15 ++------------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index e168b84..4034ffc 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -36,9 +36,14 @@ namespace PolyChat { UIController = uiController; OwnIP = getIP(); - loadChats(); + //loadChats(); //SaveChats("10", "{das ist ein test}"); Serve(); + + // test + UIController.OnIncomingConnection("1.1.1.1"); + UIController.OnIncomingConnection("1.2.3.4"); + UIController.OnIncomingConnection("1.2.4.8"); } public void Connect(string ip) @@ -69,7 +74,7 @@ namespace PolyChat { Debug.WriteLine("--- Client connected! ---"); // setup event listeners - socket.On("initial", async (JToken[] data) => + socket.On("initial", (JToken[] data) => { Debug.WriteLine("--- initial packet received ---"); string ForeignIp = data[0].ToString(); diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 06a9db4..df1d05b 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -26,6 +26,7 @@ namespace PolyChat private ObservableCollection Partners; private ChatPartner selectedPartner = null; private string username; + public MainPage() { this.InitializeComponent(); @@ -80,6 +81,14 @@ namespace PolyChat public async void OnOpenNewChatDialog(object sender = null, RoutedEventArgs e = null) { + OnIncomingMessage( + "1.1.1.1", + new JObject( + new JProperty("type", "username"), + new JProperty("content", "Cloudflare") + ).ToString(), + DateTime.Now + ); NewChatDialog dialog = new NewChatDialog(); var result = await dialog.ShowAsync(); if (result == ContentDialogResult.Primary) @@ -138,7 +147,10 @@ namespace PolyChat { case "username": Debug.WriteLine($"! username change for {sendingPartner.Code} -> {content}"); - sendingPartner.SetName(Name); + sendingPartner.Name = content; + int index = Partners.IndexOf(sendingPartner); + Partners.Remove(sendingPartner); + Partners.Insert(index, sendingPartner); break; default: sendingPartner.AddMessage(new ChatMessage(origin, type, content, timeStamp)); @@ -159,7 +171,7 @@ namespace PolyChat origin, item["type"].ToString(), item["content"].ToString()//, - //DateTime.Parse(item["timestamp"].ToString()) + //DateTime.Parse(item["timestamp"].ToString()) ) ); } diff --git a/PolyChat/Models/ChatPartner.cs b/PolyChat/Models/ChatPartner.cs index 32df3be..abc5f00 100644 --- a/PolyChat/Models/ChatPartner.cs +++ b/PolyChat/Models/ChatPartner.cs @@ -1,17 +1,17 @@ using SocketIOSharp.Server.Client; using System.Collections.ObjectModel; using System.ComponentModel; +using System.Diagnostics; using System.Runtime.CompilerServices; namespace PolyChat.Models { - public class ChatPartner : INotifyPropertyChanged + public class ChatPartner { public string Name; public string Code; public ObservableCollection Messages; private SocketIOSocket socketIOSocket; - public event PropertyChangedEventHandler PropertyChanged; public ChatPartner(string name, string code, ObservableCollection messages = null) { @@ -23,20 +23,9 @@ namespace PolyChat.Models public SocketIOSocket SocketIOSocket { get => socketIOSocket; set => socketIOSocket = value; } - private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - public void AddMessage(ChatMessage message) { Messages.Add(message); } - - public void SetName(string name) - { - Name = name; - NotifyPropertyChanged("Name"); - } } } \ No newline at end of file From 3cf0197672384afa9aed5dd181cf85b914dd6f21 Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 12:55:31 +0200 Subject: [PATCH 65/82] stuff --- PolyChat/Controller.cs | 104 +++++++++++++++++++++++---------- PolyChat/MainPage.xaml.cs | 4 ++ PolyChat/Models/ChatMessage.cs | 5 -- 3 files changed, 76 insertions(+), 37 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 4034ffc..99ac8ac 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -36,8 +36,6 @@ namespace PolyChat { UIController = uiController; OwnIP = getIP(); - //loadChats(); - //SaveChats("10", "{das ist ein test}"); Serve(); // test @@ -59,7 +57,7 @@ namespace PolyChat { Connections.Add(ip, new Connection(ip, PORT, Data => OnMessage(ip, Data), CloseChat)); } - + } private void Serve() @@ -78,6 +76,7 @@ namespace PolyChat { Debug.WriteLine("--- initial packet received ---"); string ForeignIp = data[0].ToString(); + Debug.WriteLine($"--- this ip was in the inital packet: {ForeignIp} ---"); if (isInConnections(ForeignIp)) { @@ -178,6 +177,7 @@ namespace PolyChat /// public void loadChats() { + //TODO: also load chatlogs when user tries to connect //load dir and create if non existant if (Directory.Exists("U:\\PolyChat\\Saves")) { @@ -206,9 +206,43 @@ namespace PolyChat UIController.OnIncomingMessages(ip, jsonArr); } } - } + /* + public void loadChat(String ip) + { + //TODO: also load chatlogs when user tries to connect + //load dir and create if non existant + if (Directory.Exists("U:\\PolyChat\\Saves")) + { + Debug.WriteLine("--Path exists.--"); + } + else + { + Directory.CreateDirectory("U:\\PolyChat\\Saves"); + Debug.WriteLine("--Path Created--."); + } + + //go through all files and send ip and json array to ui + String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); + if (filepaths.Length > 0) + { + Debug.WriteLine("---Loading Saves"); + foreach (String path in filepaths) + { + Debug.WriteLine($"--{path}"); + String jsonArr = File.ReadAllText(path); + String ip = Path.GetFileName(path); + ip = ip.Substring(0, ip.Length - 4); + Debug.WriteLine($"-{ip}"); + Debug.WriteLine(jsonArr); + Connect(ip); + UIController.OnIncomingConnection(ip); + UIController.OnIncomingMessages(ip, jsonArr); + } + } + } + */ /// /// Saves incoming chat message to /// @@ -221,43 +255,51 @@ namespace PolyChat //writing flag setzen oder auch in der datei selbst ne flag setzen new Thread(() => { - if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) + //breaking if namechange + JObject obj = JObject.Parse(json); + if (!obj["type"].ToString().Equals("username")) { - Debug.WriteLine("--File allready exists--"); - //check for integraty of file - string output = File.ReadAllText($"U:\\PolyChat\\Saves\\{ip}.txt"); - Debug.WriteLine($"---{output}---"); - if (output.Substring(0, 1).Equals("[") && output.Substring(output.Length - 1, 1).Equals("]")) + //adding timestamp + obj = JObject.Parse(json); + obj.Add(new JProperty("timestamp", timeStamp)); + json = obj.ToString(); + if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) { - Debug.WriteLine("--adding new chatmessage--"); - //structure intact - JObject obj = JObject.Parse(json); - //save new chat - String saved = output.Substring(0, output.Length - 1); - output = saved + ", " + json + " ]"; - File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); - File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", output); + Debug.WriteLine("--File allready exists--"); + //check for integraty of file + string output = File.ReadAllText($"U:\\PolyChat\\Saves\\{ip}.txt"); + Debug.WriteLine($"---{output}---"); + if (output.Substring(0, 1).Equals("[") && output.Substring(output.Length - 1, 1).Equals("]")) + { + Debug.WriteLine("--adding new chatmessage--"); + //structure intact + //save new chat + String saved = output.Substring(0, output.Length - 1); + output = saved + ", " + json + " ]"; + File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", output); + } + else + { + Debug.WriteLine("--Structure not intact--"); + Debug.WriteLine("--redoing file--"); + //structure not intact + //redo file + File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", $"[ {json} ]"); + } } else { - Debug.WriteLine("--Structure not intact--"); - Debug.WriteLine("--redoing file--"); - //structure not intact - //redo file - File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); + Debug.WriteLine("--Creating new File--"); + //setup file File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", $"[ {json} ]"); } } - else - { - Debug.WriteLine("--Creating new File--"); - //setup file - File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", $"[ {json} ]"); - } }).Start(); } - private void encode(string json) + private void encode(string json) { byte[] plaintext = Encoding.UTF8.GetBytes(json); byte[] entropy = new byte[20]; @@ -266,8 +308,6 @@ namespace PolyChat rng.GetBytes(entropy); } - /*byte[] ciphertext = ProtectedData.Protect(plaintext, entropy, - DataProtectionScope.CurrentUser);*/ } } } diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index df1d05b..f918ff3 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -171,7 +171,11 @@ namespace PolyChat origin, item["type"].ToString(), item["content"].ToString()//, +<<<<<<< HEAD //DateTime.Parse(item["timestamp"].ToString()) +======= + //DateTime.Parse(item["timestamp"].ToString()) +>>>>>>> be4eada (stuff) ) ); } diff --git a/PolyChat/Models/ChatMessage.cs b/PolyChat/Models/ChatMessage.cs index 4056559..7428068 100644 --- a/PolyChat/Models/ChatMessage.cs +++ b/PolyChat/Models/ChatMessage.cs @@ -11,12 +11,7 @@ namespace PolyChat.Models public string Content; public DateTime TimeStamp; public readonly bool Foreign; - - public ChatMessage() - { - - } /// /// Create own Message (directly sent) /// From a8a07f44c3f33e219a0dceb5da383f7a9b25c0e3 Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 13:33:03 +0200 Subject: [PATCH 66/82] brgin of encryption --- PolyChat/Controller.cs | 52 ++++----------------- PolyChat/Models/FileManager.cs | 84 ++++++++++++++++++++++++++++++++++ PolyChat/PolyChat.csproj | 1 + 3 files changed, 95 insertions(+), 42 deletions(-) create mode 100644 PolyChat/Models/FileManager.cs diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 99ac8ac..8de1f25 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -10,6 +10,9 @@ using System.Threading; using System; using System.Text; using System.Security.Cryptography; +using Windows.Security.Cryptography.Core; +using Windows.Security.Cryptography; +using Windows.Storage.Streams; namespace PolyChat { @@ -36,6 +39,8 @@ namespace PolyChat { UIController = uiController; OwnIP = getIP(); + loadChats(); + encode("test"); Serve(); // test @@ -173,6 +178,8 @@ namespace PolyChat /// /// sends chatlogs as json array to uiController wit corrosponding ip + /// + /// in ui when chat is clicked connection gets established /// /// public void loadChats() @@ -208,41 +215,6 @@ namespace PolyChat } } - /* - public void loadChat(String ip) - { - //TODO: also load chatlogs when user tries to connect - //load dir and create if non existant - if (Directory.Exists("U:\\PolyChat\\Saves")) - { - Debug.WriteLine("--Path exists.--"); - } - else - { - Directory.CreateDirectory("U:\\PolyChat\\Saves"); - Debug.WriteLine("--Path Created--."); - } - - //go through all files and send ip and json array to ui - String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); - if (filepaths.Length > 0) - { - Debug.WriteLine("---Loading Saves"); - foreach (String path in filepaths) - { - Debug.WriteLine($"--{path}"); - String jsonArr = File.ReadAllText(path); - String ip = Path.GetFileName(path); - ip = ip.Substring(0, ip.Length - 4); - Debug.WriteLine($"-{ip}"); - Debug.WriteLine(jsonArr); - Connect(ip); - UIController.OnIncomingConnection(ip); - UIController.OnIncomingMessages(ip, jsonArr); - } - } - } - */ /// /// Saves incoming chat message to /// @@ -301,13 +273,9 @@ namespace PolyChat private void encode(string json) { - byte[] plaintext = Encoding.UTF8.GetBytes(json); - byte[] entropy = new byte[20]; - using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider()) - { - rng.GetBytes(entropy); - } - + String ecryptetText = FileManager.encrypt(json); + Debug.WriteLine(ecryptetText); + //Debug.WriteLine(FileManager.decrypt(ecryptetText)); } } } diff --git a/PolyChat/Models/FileManager.cs b/PolyChat/Models/FileManager.cs new file mode 100644 index 0000000..815873c --- /dev/null +++ b/PolyChat/Models/FileManager.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using System.Security.Cryptography; + + +namespace PolyChat.Models +{ + class FileManager + { + public static String encrypt(String toEncrypt) + { + try { + string textToEncrypt = toEncrypt; + string ToReturn = ""; + string publickey = "santhosh"; + string secretkey = "engineer"; + byte[] secretkeyByte = { }; + secretkeyByte = System.Text.Encoding.UTF8.GetBytes(secretkey); + byte[] publickeybyte = { }; + publickeybyte = System.Text.Encoding.UTF8.GetBytes(publickey); + MemoryStream ms = null; + CryptoStream cs = null; + byte[] inputbyteArray = System.Text.Encoding.UTF8.GetBytes(textToEncrypt); + using (DESCryptoServiceProvider des = new DESCryptoServiceProvider()) + { + ms = new MemoryStream(); + cs = new CryptoStream(ms, des.CreateEncryptor(publickeybyte, secretkeyByte), CryptoStreamMode.Write); + cs.Write(inputbyteArray, 0, inputbyteArray.Length); + cs.FlushFinalBlock(); + ToReturn = Convert.ToBase64String(ms.ToArray()); + } + return ToReturn; + } catch (Exception ex) + { + throw new Exception(ex.Message, ex.InnerException); + } + + } + + + public static String decrypt(String toDecrypt) + { + try + { + string textToDecrypt = toDecrypt; + string ToReturn = ""; + string publickey = "santhosh"; + string privatekey = "engineer"; + byte[] privatekeyByte = { }; + privatekeyByte = System.Text.Encoding.UTF8.GetBytes(privatekey); + byte[] publickeybyte = { }; + publickeybyte = System.Text.Encoding.UTF8.GetBytes(publickey); + MemoryStream ms = null; + CryptoStream cs = null; + byte[] inputbyteArray = new byte[textToDecrypt.Replace(" ", "+").Length]; + inputbyteArray = Convert.FromBase64String(textToDecrypt.Replace(" ", "+")); + using (DESCryptoServiceProvider des = new DESCryptoServiceProvider()) + { + ms = new MemoryStream(); + cs = new CryptoStream(ms, des.CreateDecryptor(publickeybyte, privatekeyByte), CryptoStreamMode.Write); + cs.Write(inputbyteArray, 0, inputbyteArray.Length); + cs.FlushFinalBlock(); + Encoding encoding = Encoding.UTF8; + ToReturn = encoding.GetString(ms.ToArray()); + } + return ToReturn; + } + catch (Exception ae) + { + throw new Exception(ae.Message, ae.InnerException); + } + } + } + + + + + + +} diff --git a/PolyChat/PolyChat.csproj b/PolyChat/PolyChat.csproj index 12041a4..c047509 100644 --- a/PolyChat/PolyChat.csproj +++ b/PolyChat/PolyChat.csproj @@ -137,6 +137,7 @@ + From 0d76ec30e2df25a418a83aaf07bb0eec7eaff872 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 13:29:04 +0200 Subject: [PATCH 67/82] only open dialog if there are no popups already open --- PolyChat/MainPage.xaml.cs | 29 +++++++++++++++++++++++++---- PolyChat/PolyChat.csproj | 3 --- PolyChat/Views/Dialog.xaml.cs | 3 ++- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index f918ff3..e35e422 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -7,11 +7,11 @@ using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; using System.Text.Json; -using System.Threading.Tasks; +using Windows.Foundation; using Windows.UI.Core; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Data; +using Windows.UI.Xaml.Media; // The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 @@ -90,7 +90,7 @@ namespace PolyChat DateTime.Now ); NewChatDialog dialog = new NewChatDialog(); - var result = await dialog.ShowAsync(); + var result = await SafelyOpenDialog(dialog); if (result == ContentDialogResult.Primary) { string ip = IP.GetIPFromCode(dialog.getValue()); @@ -106,7 +106,7 @@ namespace PolyChat public async void OnOpenEditUsernameDialog(object sender = null, RoutedEventArgs e = null) { EditUsernameDialog dialog = new EditUsernameDialog(username); - var result = await dialog.ShowAsync(); + var result = await SafelyOpenDialog(dialog); if (result == ContentDialogResult.Primary) { username = dialog.getValue(); @@ -220,6 +220,27 @@ namespace PolyChat } } + public static IAsyncOperation SafelyOpenDialog(Dialog d) + { + if(VisualTreeHelper.GetOpenPopups(Window.Current).Count == 0) + return d.ShowAsync(); + return null; + } + + public static IAsyncOperation SafelyOpenDialog(NewChatDialog d) + { + if (VisualTreeHelper.GetOpenPopups(Window.Current).Count == 0) + return d.ShowAsync(); + return null; + } + + public static IAsyncOperation SafelyOpenDialog(EditUsernameDialog d) + { + if (VisualTreeHelper.GetOpenPopups(Window.Current).Count == 0) + return d.ShowAsync(); + return null; + } + // UPDATE FUNCTIONS FOR UI PLACEHOLDERS private void updateNoChatsPlaceholder() diff --git a/PolyChat/PolyChat.csproj b/PolyChat/PolyChat.csproj index c047509..df5e9f4 100644 --- a/PolyChat/PolyChat.csproj +++ b/PolyChat/PolyChat.csproj @@ -204,9 +204,6 @@ 5.0.2 - - - 14.0 diff --git a/PolyChat/Views/Dialog.xaml.cs b/PolyChat/Views/Dialog.xaml.cs index f342693..331fb6f 100644 --- a/PolyChat/Views/Dialog.xaml.cs +++ b/PolyChat/Views/Dialog.xaml.cs @@ -3,6 +3,7 @@ using PolyChat.Util; using System; using System.Collections.ObjectModel; using System.Linq; +using System.Threading; using Windows.Foundation; using Windows.UI.Core; using Windows.UI.Popups; @@ -34,7 +35,7 @@ namespace PolyChat.Views Primary = primary.Action; Secondary = secondary.Action; // show - ShowDialogAsync(); + MainPage.SafelyOpenDialog(this); } private void setType(string type, string message) From 59d48279654002a2653efabf1040fe04bdba4686 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 14:04:39 +0200 Subject: [PATCH 68/82] removed test code for username reactivitiy --- PolyChat/Controller.cs | 2 ++ PolyChat/MainPage.xaml.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 8de1f25..214c034 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -44,9 +44,11 @@ namespace PolyChat Serve(); // test + /* UIController.OnIncomingConnection("1.1.1.1"); UIController.OnIncomingConnection("1.2.3.4"); UIController.OnIncomingConnection("1.2.4.8"); + */ } public void Connect(string ip) diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index e35e422..f5a238e 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -81,6 +81,7 @@ namespace PolyChat public async void OnOpenNewChatDialog(object sender = null, RoutedEventArgs e = null) { + /* OnIncomingMessage( "1.1.1.1", new JObject( @@ -89,6 +90,7 @@ namespace PolyChat ).ToString(), DateTime.Now ); + */ NewChatDialog dialog = new NewChatDialog(); var result = await SafelyOpenDialog(dialog); if (result == ContentDialogResult.Primary) From 179ce2b1bbcd990d8e9aa1332584f8981a753b5d Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 14:36:21 +0200 Subject: [PATCH 69/82] Dark Theme, added styling for messages --- PolyChat/MainPage.xaml | 12 ++++++------ PolyChat/MainPage.xaml.cs | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/PolyChat/MainPage.xaml b/PolyChat/MainPage.xaml index e2d66c4..596ea2b 100644 --- a/PolyChat/MainPage.xaml +++ b/PolyChat/MainPage.xaml @@ -6,7 +6,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:models="using:PolyChat.Models" mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> + Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" RequestedTheme="Dark"> @@ -97,13 +97,13 @@ - + - - - - + + + + diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index f5a238e..b669e8e 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -81,6 +81,7 @@ namespace PolyChat public async void OnOpenNewChatDialog(object sender = null, RoutedEventArgs e = null) { + // test /* OnIncomingMessage( "1.1.1.1", From 9141efd55a561e088168655826b252de7cb3a7b5 Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 14:07:07 +0200 Subject: [PATCH 70/82] Propper file manager with encryption of files --- PolyChat/Controller.cs | 106 ++------------------------- PolyChat/Models/FileManager.cs | 127 ++++++++++++++++++++++++++++++++- 2 files changed, 131 insertions(+), 102 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 214c034..c15fac1 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -26,6 +26,7 @@ namespace PolyChat private const ushort PORT = 8050; // Controller private readonly MainPage UIController; + private readonly FileManager fileManager; // Props private Dictionary Connections = new Dictionary(); private string OwnName = ""; @@ -38,9 +39,9 @@ namespace PolyChat public Controller(MainPage uiController) { UIController = uiController; + fileManager = new FileManager(uiController); OwnIP = getIP(); - loadChats(); - encode("test"); + fileManager.loadChats(); Serve(); // test @@ -120,7 +121,7 @@ namespace PolyChat // send as json Connections[ip].SendMessage(json.ToString()); // save to logs - SaveChats(ip, json.ToString(), DateTime.Now); + fileManager.saveChats(ip, json.ToString(), DateTime.Now); } private void OnMessage(string ip, JToken[] data) @@ -131,7 +132,7 @@ namespace PolyChat DateTime now = DateTime.Now; Debug.WriteLine("RAW: " + data[0]); UIController.OnIncomingMessage(ip, data[0].ToString(), now); - SaveChats(ip, data[0].ToString(), now); + fileManager.saveChats(ip, data[0].ToString(), now); } else Debug.WriteLine("Undefined: " + data); } @@ -178,106 +179,11 @@ namespace PolyChat return null; } - /// - /// sends chatlogs as json array to uiController wit corrosponding ip - /// - /// in ui when chat is clicked connection gets established - /// - /// - public void loadChats() - { - //TODO: also load chatlogs when user tries to connect - //load dir and create if non existant - if (Directory.Exists("U:\\PolyChat\\Saves")) - { - Debug.WriteLine("--Path exists.--"); - } - else - { - Directory.CreateDirectory("U:\\PolyChat\\Saves"); - Debug.WriteLine("--Path Created--."); - } - - //go through all files and send ip and json array to ui - String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); - if (filepaths.Length > 0) - { - Debug.WriteLine("---Loading Saves"); - foreach (String path in filepaths) - { - Debug.WriteLine($"--{path}"); - String jsonArr = File.ReadAllText(path); - String ip = Path.GetFileName(path); - ip = ip.Substring(0, ip.Length - 4); - Debug.WriteLine($"-{ip}"); - Debug.WriteLine(jsonArr); - UIController.OnIncomingConnection(ip); - UIController.OnIncomingMessages(ip, jsonArr); - } - } - } - - /// - /// Saves incoming chat message to - /// - /// - /// - public void SaveChats(string ip, string json, DateTime timeStamp) - { - //Vielleicht noch so machen dass die mit gleicher ip nacheinander gemacht - //werden damit es nicht zu überschreibungen kommt vielleicth auch ganz oben oder am ende ne - //writing flag setzen oder auch in der datei selbst ne flag setzen - new Thread(() => - { - //breaking if namechange - JObject obj = JObject.Parse(json); - if (!obj["type"].ToString().Equals("username")) - { - //adding timestamp - obj = JObject.Parse(json); - obj.Add(new JProperty("timestamp", timeStamp)); - json = obj.ToString(); - if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) - { - Debug.WriteLine("--File allready exists--"); - //check for integraty of file - string output = File.ReadAllText($"U:\\PolyChat\\Saves\\{ip}.txt"); - Debug.WriteLine($"---{output}---"); - if (output.Substring(0, 1).Equals("[") && output.Substring(output.Length - 1, 1).Equals("]")) - { - Debug.WriteLine("--adding new chatmessage--"); - //structure intact - //save new chat - String saved = output.Substring(0, output.Length - 1); - output = saved + ", " + json + " ]"; - File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); - File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", output); - } - else - { - Debug.WriteLine("--Structure not intact--"); - Debug.WriteLine("--redoing file--"); - //structure not intact - //redo file - File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); - File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", $"[ {json} ]"); - } - } - else - { - Debug.WriteLine("--Creating new File--"); - //setup file - File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", $"[ {json} ]"); - } - } - }).Start(); - } - private void encode(string json) { String ecryptetText = FileManager.encrypt(json); Debug.WriteLine(ecryptetText); - //Debug.WriteLine(FileManager.decrypt(ecryptetText)); + Debug.WriteLine(FileManager.decrypt(ecryptetText)); } } } diff --git a/PolyChat/Models/FileManager.cs b/PolyChat/Models/FileManager.cs index 815873c..3702fd5 100644 --- a/PolyChat/Models/FileManager.cs +++ b/PolyChat/Models/FileManager.cs @@ -5,12 +5,131 @@ using System.Text; using System.Threading.Tasks; using System.IO; using System.Security.Cryptography; - +using System.Diagnostics; +using System.Threading; +using Newtonsoft.Json.Linq; namespace PolyChat.Models { class FileManager { + // Controller + private readonly MainPage UIController; + + public FileManager(MainPage uiController) + { + UIController = uiController; + } + + /// + /// sends chatlogs as json array to uiController wit corrosponding ip + /// + /// in ui when chat is clicked connection gets established + /// + /// + public void loadChats() + { + //load dir and create if non existant + if (Directory.Exists("U:\\PolyChat\\Saves")) + { + Debug.WriteLine("--Path exists.--"); + } + else + { + Directory.CreateDirectory("U:\\PolyChat\\Saves"); + Debug.WriteLine("--Path Created--."); + } + + //go through all files and send ip and json array to ui + String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); + if (filepaths.Length > 0) + { + Debug.WriteLine("---Loading Saves"); + foreach (String path in filepaths) + { + Debug.WriteLine($"--{path}"); + String jsonArr = decrypt(File.ReadAllText(path)); + String ip = Path.GetFileName(path); + ip = ip.Substring(0, ip.Length - 4); + Debug.WriteLine($"-{ip}"); + Debug.WriteLine(jsonArr); + UIController.OnIncomingConnection(ip); + UIController.OnIncomingMessages(ip, jsonArr); + } + } + } + + /// + /// Saves incoming chat message to + /// + /// + /// + public void saveChats(string ip, string json, DateTime timeStamp) + { + //Vielleicht noch so machen dass die mit gleicher ip nacheinander gemacht + //werden damit es nicht zu überschreibungen kommt vielleicth auch ganz oben oder am ende ne + //writing flag setzen oder auch in der datei selbst ne flag setzen + new Thread(() => + { + //breaking if namechange + JObject obj = JObject.Parse(json); + if (!obj["type"].ToString().Equals("username")) + { + //adding timestamp + obj = JObject.Parse(json); + obj.Add(new JProperty("timestamp", timeStamp)); + json = obj.ToString(); + if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) + { + Debug.WriteLine("--File allready exists--"); + //check for integraty of file + string output = decrypt(File.ReadAllText($"U:\\PolyChat\\Saves\\{ip}.txt")); + Debug.WriteLine($"---{output}---"); + if (output.Substring(0, 1).Equals("[") && output.Substring(output.Length - 1, 1).Equals("]")) + { + Debug.WriteLine("--adding new chatmessage--"); + //structure intact + //save new chat + String saved = output.Substring(0, output.Length - 1); + output = saved + ", " + json + " ]"; + File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", encrypt(output)); + } + else + { + Debug.WriteLine("--Structure not intact--"); + Debug.WriteLine("--redoing file--"); + //structure not intact + //redo file + File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", encrypt($"[ {json} ]")); + } + } + else + { + Debug.WriteLine("--Creating new File--"); + //setup file + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", encrypt($"[ {json} ]")); + } + } + }).Start(); + } + + + //--------------------------------------------------------------------------------------------------- + //security + //--------------------------------------------------------------------------------------------------- + private void genKeys() + { + + } + + + /// + /// does exactly what it says. XD + /// + /// + /// public static String encrypt(String toEncrypt) { try { @@ -41,7 +160,11 @@ namespace PolyChat.Models } - + /// + /// does exactly what it says. XD + /// + /// + /// public static String decrypt(String toDecrypt) { try From c3daaf8b003ba1366cfd70889fb769f322b84c0b Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 14:26:35 +0200 Subject: [PATCH 71/82] Added method to delete userspeciffic logs. --- PolyChat/Controller.cs | 2 ++ PolyChat/Models/FileManager.cs | 61 ++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index c15fac1..a7c5eca 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -93,8 +93,10 @@ namespace PolyChat } else { + Debug.WriteLine("---- Added new Connection ----");//Todo show error! Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data), CloseChat)); UIController.OnIncomingConnection(ForeignIp); + fileManager.loadChats(ForeignIp); } }); }); diff --git a/PolyChat/Models/FileManager.cs b/PolyChat/Models/FileManager.cs index 3702fd5..ec1485f 100644 --- a/PolyChat/Models/FileManager.cs +++ b/PolyChat/Models/FileManager.cs @@ -21,6 +21,67 @@ namespace PolyChat.Models UIController = uiController; } + /// + /// deletes chatlog of one speciffic user + /// + /// + public void deleteChat(String ip) + { + if (Directory.Exists("U:\\PolyChat\\Saves")) + { + Debug.WriteLine("--Path exists.--"); + //go through all files and send ip and json array to ui + String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); + if (filepaths.Length > 0) + { + foreach (String path in filepaths) + { + if(Path.GetFileName(path).Equals(ip)) + { + File.Delete(path); + return; + } + } + } + } + } + + /// + /// loads one chatlog probably when someone tries to connect + /// + /// + public void loadChats() + { + //load dir and create if non existant + if (Directory.Exists("U:\\PolyChat\\Saves")) + { + Debug.WriteLine("--Path exists.--"); + } + else + { + Directory.CreateDirectory("U:\\PolyChat\\Saves"); + Debug.WriteLine("--Path Created--."); + } + + //go through all files and send ip and json array to ui + String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); + if (filepaths.Length > 0) + { + Debug.WriteLine("---Loading Saves"); + foreach (String path in filepaths) + { + Debug.WriteLine($"--{path}"); + String jsonArr = decrypt(File.ReadAllText(path)); + String ip = Path.GetFileName(path); + ip = ip.Substring(0, ip.Length - 4); + Debug.WriteLine($"-{ip}"); + Debug.WriteLine(jsonArr); + UIController.OnIncomingConnection(ip); + UIController.OnIncomingMessages(ip, jsonArr); + } + } + } + /// /// sends chatlogs as json array to uiController wit corrosponding ip /// From 5ce1316245e3a7c5f2e3ccfa8281f5beb655e204 Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 14:32:22 +0200 Subject: [PATCH 72/82] load chatlogs on newly created chat --- PolyChat/Controller.cs | 4 ++-- PolyChat/Models/FileManager.cs | 37 ++++++++++------------------------ 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index a7c5eca..d372a12 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -93,10 +93,10 @@ namespace PolyChat } else { - Debug.WriteLine("---- Added new Connection ----");//Todo show error! + Debug.WriteLine("---- Added new Connection ----"); Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data), CloseChat)); UIController.OnIncomingConnection(ForeignIp); - fileManager.loadChats(ForeignIp); + fileManager.loadChat(ForeignIp); } }); }); diff --git a/PolyChat/Models/FileManager.cs b/PolyChat/Models/FileManager.cs index ec1485f..474aee2 100644 --- a/PolyChat/Models/FileManager.cs +++ b/PolyChat/Models/FileManager.cs @@ -36,7 +36,7 @@ namespace PolyChat.Models { foreach (String path in filepaths) { - if(Path.GetFileName(path).Equals(ip)) + if (Path.GetFileName(path).Equals(ip)) { File.Delete(path); return; @@ -50,32 +50,15 @@ namespace PolyChat.Models /// loads one chatlog probably when someone tries to connect /// /// - public void loadChats() + public void loadChat(String ip) { //load dir and create if non existant if (Directory.Exists("U:\\PolyChat\\Saves")) { Debug.WriteLine("--Path exists.--"); - } - else - { - Directory.CreateDirectory("U:\\PolyChat\\Saves"); - Debug.WriteLine("--Path Created--."); - } - - //go through all files and send ip and json array to ui - String[] filepaths = Directory.GetFiles("U:\\PolyChat\\Saves"); - if (filepaths.Length > 0) - { - Debug.WriteLine("---Loading Saves"); - foreach (String path in filepaths) + if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) { - Debug.WriteLine($"--{path}"); - String jsonArr = decrypt(File.ReadAllText(path)); - String ip = Path.GetFileName(path); - ip = ip.Substring(0, ip.Length - 4); - Debug.WriteLine($"-{ip}"); - Debug.WriteLine(jsonArr); + String jsonArr = decrypt(File.ReadAllText($"U:\\PolyChat\\Saves\\{ip}.txt")); UIController.OnIncomingConnection(ip); UIController.OnIncomingMessages(ip, jsonArr); } @@ -180,9 +163,9 @@ namespace PolyChat.Models //--------------------------------------------------------------------------------------------------- //security //--------------------------------------------------------------------------------------------------- - private void genKeys() - { - + private void genKeys() + { + } @@ -193,7 +176,8 @@ namespace PolyChat.Models /// public static String encrypt(String toEncrypt) { - try { + try + { string textToEncrypt = toEncrypt; string ToReturn = ""; string publickey = "santhosh"; @@ -214,7 +198,8 @@ namespace PolyChat.Models ToReturn = Convert.ToBase64String(ms.ToArray()); } return ToReturn; - } catch (Exception ex) + } + catch (Exception ex) { throw new Exception(ex.Message, ex.InnerException); } From 4c0413e7d838aa333860d4f4b01abc3cd801ba4a Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 14:54:25 +0200 Subject: [PATCH 73/82] removed ability to send empty messages --- PolyChat/MainPage.xaml.cs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index b669e8e..c63d534 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -217,33 +217,19 @@ namespace PolyChat private void OnKeyUp(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e) { updateSendButtonEnabled(); - if (e.Key == Windows.System.VirtualKey.Enter) + if (buttonSend.IsEnabled && e.Key == Windows.System.VirtualKey.Enter) { OnSendMessage(); } } - public static IAsyncOperation SafelyOpenDialog(Dialog d) + public static IAsyncOperation SafelyOpenDialog(ContentDialog d) { if(VisualTreeHelper.GetOpenPopups(Window.Current).Count == 0) return d.ShowAsync(); return null; } - public static IAsyncOperation SafelyOpenDialog(NewChatDialog d) - { - if (VisualTreeHelper.GetOpenPopups(Window.Current).Count == 0) - return d.ShowAsync(); - return null; - } - - public static IAsyncOperation SafelyOpenDialog(EditUsernameDialog d) - { - if (VisualTreeHelper.GetOpenPopups(Window.Current).Count == 0) - return d.ShowAsync(); - return null; - } - // UPDATE FUNCTIONS FOR UI PLACEHOLDERS private void updateNoChatsPlaceholder() From 5c4b4373d6b2aa7f3128e9c0cb04a2bb8fe37b71 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 15:31:30 +0200 Subject: [PATCH 74/82] Added Themes + Toggle --- PolyChat/MainPage.xaml | 16 +++++++--------- PolyChat/MainPage.xaml.cs | 16 +++++++++++++++- PolyChat/Views/Dialog.xaml.cs | 1 + PolyChat/Views/EditUsernameDialog.xaml.cs | 1 + PolyChat/Views/NewChatDialog.xaml.cs | 1 + 5 files changed, 25 insertions(+), 10 deletions(-) diff --git a/PolyChat/MainPage.xaml b/PolyChat/MainPage.xaml index 596ea2b..3ec2199 100644 --- a/PolyChat/MainPage.xaml +++ b/PolyChat/MainPage.xaml @@ -6,17 +6,18 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:models="using:PolyChat.Models" mc:Ignorable="d" - Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" RequestedTheme="Dark"> + Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> - + + @@ -37,7 +38,7 @@ - + @@ -55,12 +56,8 @@ - - - - - - + + diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index c63d534..f8af190 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -26,6 +26,8 @@ namespace PolyChat private ObservableCollection Partners; private ChatPartner selectedPartner = null; private string username; + private static ElementTheme Theme = ElementTheme.Light; + public MainPage() { @@ -35,6 +37,9 @@ namespace PolyChat // ui variables ipAddress.Text = IP.GetCodeFromIP(Controller.getIP()); Partners = new ObservableCollection(); + // theming + RequestedTheme = Theme; + // updated placeholder updateNoChatsPlaceholder(); updateNoUsernamePlaceholder(); updateNoChatSelected(); @@ -185,7 +190,6 @@ namespace PolyChat }); } - private void OnDeleteChat(object sender = null, RoutedEventArgs e = null) { Controller.CloseChat(selectedPartner.Code); @@ -214,6 +218,12 @@ namespace PolyChat if (!Controller.IsConnected(code)) Controller.Connect(code); } + public void OnToggleTheme(object sender, RoutedEventArgs e) + { + Theme = Theme == ElementTheme.Light ? ElementTheme.Dark : ElementTheme.Light; + RequestedTheme = Theme; + } + private void OnKeyUp(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e) { updateSendButtonEnabled(); @@ -230,6 +240,10 @@ namespace PolyChat return null; } + // GETTERS + + public static ElementTheme GetTheme() => Theme; + // UPDATE FUNCTIONS FOR UI PLACEHOLDERS private void updateNoChatsPlaceholder() diff --git a/PolyChat/Views/Dialog.xaml.cs b/PolyChat/Views/Dialog.xaml.cs index 331fb6f..75dd155 100644 --- a/PolyChat/Views/Dialog.xaml.cs +++ b/PolyChat/Views/Dialog.xaml.cs @@ -36,6 +36,7 @@ namespace PolyChat.Views Secondary = secondary.Action; // show MainPage.SafelyOpenDialog(this); + RequestedTheme = MainPage.GetTheme(); } private void setType(string type, string message) diff --git a/PolyChat/Views/EditUsernameDialog.xaml.cs b/PolyChat/Views/EditUsernameDialog.xaml.cs index faec438..8e44c1e 100644 --- a/PolyChat/Views/EditUsernameDialog.xaml.cs +++ b/PolyChat/Views/EditUsernameDialog.xaml.cs @@ -22,6 +22,7 @@ namespace PolyChat.Views if (initialValue == null || initialValue.Length == 0) IsSecondaryButtonEnabled = false; else input.Text = initialValue; validate(); + RequestedTheme = MainPage.GetTheme(); } public string getValue() diff --git a/PolyChat/Views/NewChatDialog.xaml.cs b/PolyChat/Views/NewChatDialog.xaml.cs index 4c888ba..a77d96f 100644 --- a/PolyChat/Views/NewChatDialog.xaml.cs +++ b/PolyChat/Views/NewChatDialog.xaml.cs @@ -20,6 +20,7 @@ namespace PolyChat.Views { this.InitializeComponent(); IsPrimaryButtonEnabled = false; + RequestedTheme = MainPage.GetTheme(); } public string getValue() From ac67ff38c69ca98e0920c7881140a71d3fd2ffe1 Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 16:05:20 +0200 Subject: [PATCH 75/82] Message Alignment First Try --- PolyChat/MainPage.xaml | 9 +++++---- PolyChat/MainPage.xaml.cs | 14 +++++--------- PolyChat/Models/ChatMessage.cs | 26 ++++++-------------------- 3 files changed, 16 insertions(+), 33 deletions(-) diff --git a/PolyChat/MainPage.xaml b/PolyChat/MainPage.xaml index 3ec2199..1ed214d 100644 --- a/PolyChat/MainPage.xaml +++ b/PolyChat/MainPage.xaml @@ -98,10 +98,11 @@ - - - - + + + + + diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index f8af190..77d97b7 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -28,7 +28,6 @@ namespace PolyChat private string username; private static ElementTheme Theme = ElementTheme.Light; - public MainPage() { this.InitializeComponent(); @@ -78,7 +77,7 @@ namespace PolyChat public void OnSendMessage(object sender = null, RoutedEventArgs e = null) { - selectedPartner.AddMessage(new ChatMessage(username, "message", inputSend.Text)); + selectedPartner.AddMessage(new ChatMessage(username, "message", inputSend.Text, DateTime.Now, false)); Controller.SendMessage(selectedPartner.Code, "message", inputSend.Text); // clear input inputSend.Text = ""; @@ -161,7 +160,7 @@ namespace PolyChat Partners.Insert(index, sendingPartner); break; default: - sendingPartner.AddMessage(new ChatMessage(origin, type, content, timeStamp)); + sendingPartner.AddMessage(new ChatMessage(origin, type, content, timeStamp, true)); break; } }); @@ -178,12 +177,9 @@ namespace PolyChat new ChatMessage( origin, item["type"].ToString(), - item["content"].ToString()//, -<<<<<<< HEAD - //DateTime.Parse(item["timestamp"].ToString()) -======= - //DateTime.Parse(item["timestamp"].ToString()) ->>>>>>> be4eada (stuff) + item["content"].ToString(), + DateTime.Parse(item["timestamp"].ToString()), + false // TODO: FIX !!!! ) ); } diff --git a/PolyChat/Models/ChatMessage.cs b/PolyChat/Models/ChatMessage.cs index 7428068..1f6d272 100644 --- a/PolyChat/Models/ChatMessage.cs +++ b/PolyChat/Models/ChatMessage.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Text.Json; +using Windows.UI.Xaml; namespace PolyChat.Models { @@ -10,38 +11,23 @@ namespace PolyChat.Models public string Type; public string Content; public DateTime TimeStamp; - public readonly bool Foreign; + public HorizontalAlignment Align; + private bool Foreign; /// - /// Create own Message (directly sent) - /// - /// My IP - /// Message Type - /// Message Content (not JSON) - public ChatMessage(string origin, string type, string content) - { - Origin = origin; - TimeStamp = DateTime.Now; - Type = type; - Content = content; - // TODO - Foreign = false; - Debug.WriteLine("Created Message: " + ToString()); - } - - /// - /// Create Message loaded with timestamp + /// Create Message /// /// Origin IP /// Message Type, usually "message" /// Message Content, usually plain text /// Parsed DateTime - public ChatMessage(string origin, string type, string content, DateTime timeStamp, bool foreign = false) + public ChatMessage(string origin, string type, string content, DateTime timeStamp, bool foreign) { Origin = origin; TimeStamp = timeStamp; Type = type; Content = content; + Align = foreign ? HorizontalAlignment.Right : HorizontalAlignment.Left; Foreign = foreign; Debug.WriteLine("Created Loaded Message: " + ToString()); } From f55c4a2b70df87e9ee497bf9b0882bac5c79d304 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 14:50:35 +0200 Subject: [PATCH 76/82] delete chat only on explicit button press, not on disconnect, dialog opening fixed --- PolyChat/Controller.cs | 12 ++++++---- PolyChat/MainPage.xaml.cs | 43 +++++++++++++++++----------------- PolyChat/Models/Connection.cs | 8 +++---- PolyChat/Models/FileManager.cs | 2 +- 4 files changed, 34 insertions(+), 31 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index d372a12..30048c2 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -96,7 +96,6 @@ namespace PolyChat Debug.WriteLine("---- Added new Connection ----"); Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data), CloseChat)); UIController.OnIncomingConnection(ForeignIp); - fileManager.loadChat(ForeignIp); } }); }); @@ -139,7 +138,7 @@ namespace PolyChat else Debug.WriteLine("Undefined: " + data); } - public void CloseChat(string IP, bool wasConnected = true) + public void CloseChat(string IP, bool wasConnected = true, bool delete = false) { Debug.WriteLine($"Deleting connection with IP:{IP}"); if (IP != null && Connections.ContainsKey(IP)) @@ -147,14 +146,17 @@ namespace PolyChat Connections[IP].Close(); Connections.Remove(IP); } - CloseChatUI(IP, wasConnected); + if (delete || !wasConnected) + CloseChatUI(IP, wasConnected, delete); + if (delete) + fileManager.deleteChat(IP); } - private void CloseChatUI(string IP, bool wasConnected = true) + private void CloseChatUI(string IP, bool wasConnected = true, bool delete = false) { UIController.OnChatPartnerDeleted(IP); string heading = wasConnected ? "Connection Closed" : "Connection Failed"; - if (!wasConnected) + if(!delete) UIController.ShowConnectionError(IP, heading, $"Connecting to {IP} failed..."); } diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 77d97b7..fd20db6 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -50,26 +50,27 @@ namespace PolyChat await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { Dialog dialog = new Dialog( - Dialog.TYPE_ERROR, - heading, - message, - new DialogButton( - "Retry", - () => - { - Controller.Connect(param); - Partners.Add(new ChatPartner( - "Connecting...", - param - )); - updateNoChatsPlaceholder(); - } - ), - new DialogButton( - "Ignore", - () => { /* do nothing */ } - ) - ); + Dialog.TYPE_ERROR, + heading, + message, + new DialogButton( + "Retry", + () => + { + Controller.Connect(param); + Partners.Add(new ChatPartner( + "Connecting...", + param + )); + updateNoChatsPlaceholder(); + } + ), + new DialogButton( + "Ignore", + () => { /* do nothing */ } + ) + ); + SafelyOpenDialog(dialog); }); } @@ -188,7 +189,7 @@ namespace PolyChat private void OnDeleteChat(object sender = null, RoutedEventArgs e = null) { - Controller.CloseChat(selectedPartner.Code); + Controller.CloseChat(selectedPartner.Code,delete: true); Partners.Remove(selectedPartner); updateNoChatsPlaceholder(); updateNoChatSelected(); diff --git a/PolyChat/Models/Connection.cs b/PolyChat/Models/Connection.cs index 9c985b6..5d45440 100644 --- a/PolyChat/Models/Connection.cs +++ b/PolyChat/Models/Connection.cs @@ -15,9 +15,9 @@ namespace PolyChat.Models private SocketIOSocket Socket; private bool Connected = false; private readonly string IP; - private Action DeleteConnection; + private Action DeleteConnection; - public Connection(string ip, ushort port, Action onMessage, Action onClose) + public Connection(string ip, ushort port, Action onMessage, Action onClose) { Debug.WriteLine("! CONNECTING TO SERVER !"); IP = ip; @@ -32,7 +32,7 @@ namespace PolyChat.Models Client.On("message", (Action) onMessage); } - public Connection(SocketIOSocket socket, Action onMessage, Action onClose) + public Connection(SocketIOSocket socket, Action onMessage, Action onClose) { Socket = socket; DeleteConnection = onClose; @@ -66,7 +66,7 @@ namespace PolyChat.Models { Debug.WriteLine("--- Disconnected! ---"); Debug.WriteLine($"--- Deleting Connection with IP: {IP}---"); - DeleteConnection(IP, IsConnected()); + DeleteConnection(IP, IsConnected(),false); Connected = false; } private void OnError(JToken[] data) diff --git a/PolyChat/Models/FileManager.cs b/PolyChat/Models/FileManager.cs index 474aee2..9a8045e 100644 --- a/PolyChat/Models/FileManager.cs +++ b/PolyChat/Models/FileManager.cs @@ -36,7 +36,7 @@ namespace PolyChat.Models { foreach (String path in filepaths) { - if (Path.GetFileName(path).Equals(ip)) + if (Path.GetFileName(path).Equals(ip+".txt")) { File.Delete(path); return; From fd5cb984e21fa44964aaa79721c9dc9bc2c4dbc1 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 15:18:57 +0200 Subject: [PATCH 77/82] dont open new chat window if client is already in list --- PolyChat/MainPage.xaml.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index fd20db6..2e976d2 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -103,7 +103,9 @@ namespace PolyChat { string ip = IP.GetIPFromCode(dialog.getValue()); Controller.Connect(ip); - Partners.Add(new ChatPartner( + ChatPartner pa = Partners.FirstOrDefault(p => p.Code == ip); + if (pa == null) + Partners.Add(new ChatPartner( "Connecting...", ip )); From 31ffe86f04de781335cd3e11c22067375fdcb8c0 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 15:46:13 +0200 Subject: [PATCH 78/82] remove unused variables --- PolyChat/Controller.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 30048c2..096b832 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -29,8 +29,6 @@ namespace PolyChat private readonly FileManager fileManager; // Props private Dictionary Connections = new Dictionary(); - private string OwnName = ""; - private string OwnIP; /// /// Initializes Controller with UI access @@ -40,7 +38,6 @@ namespace PolyChat { UIController = uiController; fileManager = new FileManager(uiController); - OwnIP = getIP(); fileManager.loadChats(); Serve(); From ba4d0b619e1c79560d13390dccc53c9b12a2ad0c Mon Sep 17 00:00:00 2001 From: Patrick Hellebrand Date: Thu, 23 Sep 2021 16:24:41 +0200 Subject: [PATCH 79/82] ChatMessages on THEEEEEEEEEEEEEEE RIGHT --- PolyChat/Controller.cs | 2 +- PolyChat/MainPage.xaml | 13 ++++++++----- PolyChat/MainPage.xaml.cs | 10 +++++----- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 096b832..fa5e171 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -42,8 +42,8 @@ namespace PolyChat Serve(); // test + UIController.OnIncomingConnection("localhost"); /* - UIController.OnIncomingConnection("1.1.1.1"); UIController.OnIncomingConnection("1.2.3.4"); UIController.OnIncomingConnection("1.2.4.8"); */ diff --git a/PolyChat/MainPage.xaml b/PolyChat/MainPage.xaml index 1ed214d..550dcc3 100644 --- a/PolyChat/MainPage.xaml +++ b/PolyChat/MainPage.xaml @@ -96,13 +96,16 @@ + + + - - - - - + + + diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 2e976d2..8a4388d 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -87,16 +87,16 @@ namespace PolyChat public async void OnOpenNewChatDialog(object sender = null, RoutedEventArgs e = null) { // test - /* + /**/ OnIncomingMessage( - "1.1.1.1", + "localhost", new JObject( - new JProperty("type", "username"), - new JProperty("content", "Cloudflare") + new JProperty("type", "message"), + new JProperty("content", "Test") ).ToString(), DateTime.Now ); - */ + /**/ NewChatDialog dialog = new NewChatDialog(); var result = await SafelyOpenDialog(dialog); if (result == ContentDialogResult.Primary) From 1daaa52c3649130dd9b2df81497e67156ce10c8a Mon Sep 17 00:00:00 2001 From: SCM6WE Date: Thu, 23 Sep 2021 16:03:12 +0200 Subject: [PATCH 80/82] Cleanup / Comments --- PolyChat/Controller.cs | 48 +++++++++++++++---- PolyChat/Models/FileManager.cs | 85 ++++++++++++++++++---------------- 2 files changed, 83 insertions(+), 50 deletions(-) diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index fa5e171..125a826 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -15,8 +15,7 @@ using Windows.Security.Cryptography; using Windows.Storage.Streams; namespace PolyChat -{ - +{ // 10.1.211.26 Marc // 10.1.218.90 Felix // 10.4.141.77 Pat @@ -37,7 +36,7 @@ namespace PolyChat public Controller(MainPage uiController) { UIController = uiController; - fileManager = new FileManager(uiController); + fileManager = new FileManager(this); fileManager.loadChats(); Serve(); @@ -65,6 +64,9 @@ namespace PolyChat } + /// + /// starts server for clients to connect to + /// private void Serve() { Debug.WriteLine("--- Controller.Serve ---"); @@ -107,6 +109,12 @@ namespace PolyChat } } + /// + /// Sends message to given ip + /// + /// + /// + /// public void SendMessage(string ip, string type, string content) { Debug.WriteLine("--- Controller.SendMessage ---"); @@ -121,7 +129,12 @@ namespace PolyChat // save to logs fileManager.saveChats(ip, json.ToString(), DateTime.Now); } - + + /// + /// if We recieve a message this method gets triggert + /// + /// ip from sender + /// String that is send private void OnMessage(string ip, JToken[] data) { Debug.WriteLine("--- Controller.OnMessage ---"); @@ -135,6 +148,12 @@ namespace PolyChat else Debug.WriteLine("Undefined: " + data); } + /// + /// Closes chat connection + /// + /// ip of user to be closed + /// + /// public void CloseChat(string IP, bool wasConnected = true, bool delete = false) { Debug.WriteLine($"Deleting connection with IP:{IP}"); @@ -149,6 +168,17 @@ namespace PolyChat fileManager.deleteChat(IP); } + /// + /// sends incoming message to ui + /// + /// ip of client that send the message + /// the json array that is ti be displayed in th gui + public void SendIncomingMessageUI(String ip, String jsonArr) + { + UIController.OnIncomingConnection(ip); + UIController.OnIncomingMessages(ip, jsonArr); + } + private void CloseChatUI(string IP, bool wasConnected = true, bool delete = false) { UIController.OnChatPartnerDeleted(IP); @@ -166,6 +196,10 @@ namespace PolyChat return Connections.ContainsKey(ip) && Connections[ip].IsConnected(); } + /// + /// returns your own ip that starts with 10. becuase that is our subnet + /// + /// public static string getIP() { IPHostEntry ipEntry = Dns.GetHostEntry(Dns.GetHostName()); @@ -180,11 +214,5 @@ namespace PolyChat return null; } - private void encode(string json) - { - String ecryptetText = FileManager.encrypt(json); - Debug.WriteLine(ecryptetText); - Debug.WriteLine(FileManager.decrypt(ecryptetText)); - } } } diff --git a/PolyChat/Models/FileManager.cs b/PolyChat/Models/FileManager.cs index 9a8045e..e841c29 100644 --- a/PolyChat/Models/FileManager.cs +++ b/PolyChat/Models/FileManager.cs @@ -14,13 +14,21 @@ namespace PolyChat.Models class FileManager { // Controller - private readonly MainPage UIController; + private readonly Controller controller; - public FileManager(MainPage uiController) + + //=============================================================================================================================================== + //Constructor + //=============================================================================================================================================== + public FileManager(Controller controller) { - UIController = uiController; + this.controller = controller; } + //=============================================================================================================================================== + // editing save files + //=============================================================================================================================================== + /// /// deletes chatlog of one speciffic user /// @@ -59,14 +67,13 @@ namespace PolyChat.Models if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) { String jsonArr = decrypt(File.ReadAllText($"U:\\PolyChat\\Saves\\{ip}.txt")); - UIController.OnIncomingConnection(ip); - UIController.OnIncomingMessages(ip, jsonArr); + controller.SendIncomingMessageUI(ip, jsonArr); } } } /// - /// sends chatlogs as json array to uiController wit corrosponding ip + /// sends chatlogs as json array to uiController with corrosponding ip /// /// in ui when chat is clicked connection gets established /// @@ -97,22 +104,18 @@ namespace PolyChat.Models ip = ip.Substring(0, ip.Length - 4); Debug.WriteLine($"-{ip}"); Debug.WriteLine(jsonArr); - UIController.OnIncomingConnection(ip); - UIController.OnIncomingMessages(ip, jsonArr); + controller.SendIncomingMessageUI(ip, jsonArr); } } } /// - /// Saves incoming chat message to + /// Saves incoming chat message to U:\PolyChat\Saves\ip.txt /// /// /// public void saveChats(string ip, string json, DateTime timeStamp) { - //Vielleicht noch so machen dass die mit gleicher ip nacheinander gemacht - //werden damit es nicht zu überschreibungen kommt vielleicth auch ganz oben oder am ende ne - //writing flag setzen oder auch in der datei selbst ne flag setzen new Thread(() => { //breaking if namechange @@ -120,39 +123,39 @@ namespace PolyChat.Models if (!obj["type"].ToString().Equals("username")) { //adding timestamp - obj = JObject.Parse(json); obj.Add(new JProperty("timestamp", timeStamp)); json = obj.ToString(); + if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) { Debug.WriteLine("--File allready exists--"); + //check for integraty of file string output = decrypt(File.ReadAllText($"U:\\PolyChat\\Saves\\{ip}.txt")); Debug.WriteLine($"---{output}---"); if (output.Substring(0, 1).Equals("[") && output.Substring(output.Length - 1, 1).Equals("]")) { - Debug.WriteLine("--adding new chatmessage--"); //structure intact //save new chat - String saved = output.Substring(0, output.Length - 1); - output = saved + ", " + json + " ]"; + Debug.WriteLine("--adding new chatmessage--"); + output = output.Substring(0, output.Length - 1) + ", " + json + " ]"; //rip appart and put file back together File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); - File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", encrypt(output)); + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", encrypt(output)); //encrypt and save to textfile } else { - Debug.WriteLine("--Structure not intact--"); - Debug.WriteLine("--redoing file--"); //structure not intact //redo file + Debug.WriteLine("--Structure not intact--"); + Debug.WriteLine("--redoing file--"); File.Delete($"U:\\PolyChat\\Saves\\{ip}.txt"); - File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", encrypt($"[ {json} ]")); + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", encrypt($"[ {json} ]")); //encrypt and write to file } } else { - Debug.WriteLine("--Creating new File--"); //setup file + Debug.WriteLine("--Creating new File--"); File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", encrypt($"[ {json} ]")); } } @@ -160,28 +163,35 @@ namespace PolyChat.Models } - //--------------------------------------------------------------------------------------------------- - //security - //--------------------------------------------------------------------------------------------------- - private void genKeys() + //=============================================================================================================================================== + // Encryption + //=============================================================================================================================================== + + /// + /// generates keypair [public, privte] (100% secure, trust me) + /// + /// + private String[] genKeys() { - + return new String[] {"sehrSichererKey", "nochVielSichererKey"}; } - + /// /// does exactly what it says. XD + /// + /// encrypts given string and returns unreadable string /// /// /// - public static String encrypt(String toEncrypt) + private String encrypt(String toEncrypt) { try { string textToEncrypt = toEncrypt; string ToReturn = ""; - string publickey = "santhosh"; - string secretkey = "engineer"; + string publickey = genKeys()[0]; + string secretkey = genKeys()[1]; byte[] secretkeyByte = { }; secretkeyByte = System.Text.Encoding.UTF8.GetBytes(secretkey); byte[] publickeybyte = { }; @@ -203,22 +213,23 @@ namespace PolyChat.Models { throw new Exception(ex.Message, ex.InnerException); } - } /// /// does exactly what it says. XD + /// + /// takes in unreadable string and returns infirmation /// /// /// - public static String decrypt(String toDecrypt) + private String decrypt(String toDecrypt) { try { string textToDecrypt = toDecrypt; string ToReturn = ""; - string publickey = "santhosh"; - string privatekey = "engineer"; + string publickey = genKeys()[0]; + string privatekey = genKeys()[1]; byte[] privatekeyByte = { }; privatekeyByte = System.Text.Encoding.UTF8.GetBytes(privatekey); byte[] publickeybyte = { }; @@ -244,10 +255,4 @@ namespace PolyChat.Models } } } - - - - - - } From ebaf831be7b788d026a62ebb8ebb27f1c61507b5 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Thu, 23 Sep 2021 16:22:38 +0200 Subject: [PATCH 81/82] set encryption keys --- PolyChat/Models/FileManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PolyChat/Models/FileManager.cs b/PolyChat/Models/FileManager.cs index e841c29..d0ca688 100644 --- a/PolyChat/Models/FileManager.cs +++ b/PolyChat/Models/FileManager.cs @@ -173,7 +173,7 @@ namespace PolyChat.Models /// private String[] genKeys() { - return new String[] {"sehrSichererKey", "nochVielSichererKey"}; + return new String[] {"12345678", "12345678" }; } From 125c908ef88b8005bc6de41c537897a9b58172f0 Mon Sep 17 00:00:00 2001 From: "Felix Hartmann (PEA3-Fe-FI)" Date: Fri, 24 Sep 2021 08:39:48 +0200 Subject: [PATCH 82/82] remove test code, add logo svg, add screenshots --- PolyChat/Assets/polychat_logo.svg | 82 ++++++++++++++++++++++++++++++ PolyChat/Controller.cs | 2 +- PolyChat/MainPage.xaml.cs | 4 +- screenshots/polychat_dark.PNG | Bin 0 -> 21853 bytes screenshots/polychat_light.PNG | Bin 0 -> 20764 bytes 5 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 PolyChat/Assets/polychat_logo.svg create mode 100644 screenshots/polychat_dark.PNG create mode 100644 screenshots/polychat_light.PNG diff --git a/PolyChat/Assets/polychat_logo.svg b/PolyChat/Assets/polychat_logo.svg new file mode 100644 index 0000000..b4be3c6 --- /dev/null +++ b/PolyChat/Assets/polychat_logo.svg @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/PolyChat/Controller.cs b/PolyChat/Controller.cs index 125a826..2dd2977 100644 --- a/PolyChat/Controller.cs +++ b/PolyChat/Controller.cs @@ -41,8 +41,8 @@ namespace PolyChat Serve(); // test - UIController.OnIncomingConnection("localhost"); /* + UIController.OnIncomingConnection("localhost"); UIController.OnIncomingConnection("1.2.3.4"); UIController.OnIncomingConnection("1.2.4.8"); */ diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 8a4388d..9c45126 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -87,7 +87,7 @@ namespace PolyChat public async void OnOpenNewChatDialog(object sender = null, RoutedEventArgs e = null) { // test - /**/ + /* OnIncomingMessage( "localhost", new JObject( @@ -96,7 +96,7 @@ namespace PolyChat ).ToString(), DateTime.Now ); - /**/ + */ NewChatDialog dialog = new NewChatDialog(); var result = await SafelyOpenDialog(dialog); if (result == ContentDialogResult.Primary) diff --git a/screenshots/polychat_dark.PNG b/screenshots/polychat_dark.PNG new file mode 100644 index 0000000000000000000000000000000000000000..32517331cac9e89d6a42d00de149b76cd9c06413 GIT binary patch literal 21853 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 WQxCl14FwLie6syx=12Sg{Qm%!d^e*2 literal 0 HcmV?d00001 diff --git a/screenshots/polychat_light.PNG b/screenshots/polychat_light.PNG new file mode 100644 index 0000000000000000000000000000000000000000..86c4e88f2199e472607716f6be4a61035d407ef5 GIT binary patch literal 20764 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 literal 0 HcmV?d00001