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 new file mode 100644 index 0000000..64886bf --- /dev/null +++ b/PolyChat/Controller.cs @@ -0,0 +1,211 @@ +using System.Collections.Generic; +using System.Diagnostics; +using Newtonsoft.Json.Linq; +using System.Net; +using SocketIOSharp.Server; +using SocketIOSharp.Server.Client; +using PolyChat.Models; +using System; + +namespace PolyChat +{ + // 10.1.211.26 Marc + // 10.1.218.90 Felix + // 10.4.141.77 Pat + class Controller + { + // Constants + private const ushort PORT = 8050; + // Controller + private readonly MainPage UIController; + private readonly FileManager fileManager; + // Props + private Dictionary Connections = new Dictionary(); + + /// + /// Initializes Controller with UI access + /// + /// UWP UI Controller + public Controller(MainPage uiController) + { + UIController = uiController; + fileManager = new FileManager(this); + fileManager.loadChats(); + Serve(); + + // test + /* + UIController.OnIncomingConnection("localhost"); + UIController.OnIncomingConnection("1.2.3.4"); + UIController.OnIncomingConnection("1.2.4.8"); + */ + } + + public void Connect(string ip) + { + Debug.WriteLine("--- Controller.Connect ---"); + 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)); + } + + } + + /// + /// starts server for clients to connect to + /// + private void Serve() + { + Debug.WriteLine("--- Controller.Serve ---"); + 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[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! + CloseChatUI(ForeignIp); + } + else + { + Debug.WriteLine("---- Added new Connection ----"); + Connections.Add(ForeignIp, new Connection(socket, Data => OnMessage(ForeignIp, Data), CloseChat)); + UIController.OnIncomingConnection(ForeignIp); + } + }); + }); + } + + public void SendBroadcastMessage(string type, string content) + { + Debug.WriteLine("--- Controller.Broadcast ---"); + foreach (KeyValuePair entry in Connections) + { + SendMessage(entry.Key, type, content); + } + } + + /// + /// Sends message to given ip + /// + /// + /// + /// + public void SendMessage(string ip, string type, string content) + { + Debug.WriteLine("--- Controller.SendMessage ---"); + Debug.WriteLine($"{type} -> {ip} content: {content}"); + JObject json = new JObject( + new JProperty("type", type), + new JProperty("content", content) + ); + Debug.WriteLine($"json: {json.ToString()}"); + // send as json + Connections[ip].SendMessage(json.ToString()); + // 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 ---"); + if (data != null && data.Length > 0 && data[0] != null) + { + DateTime now = DateTime.Now; + Debug.WriteLine("RAW: " + data[0]); + UIController.OnIncomingMessage(ip, data[0].ToString(), now); + fileManager.saveChats(ip, data[0].ToString(), now); + } + 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}"); + if (IP != null && Connections.ContainsKey(IP)) + { + Connections[IP].Close(); + Connections.Remove(IP); + } + if (delete || !wasConnected) + CloseChatUI(IP, wasConnected, delete); + if (delete) + 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); + string heading = wasConnected ? "Connection Closed" : "Connection Failed"; + if(!delete) + UIController.ShowConnectionError(IP, heading, $"Connecting to {IP} failed..."); + } + + private bool isInConnections(string IP) + { + return Connections.ContainsKey(IP); + } + public bool IsConnected(string ip) + { + 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()); + 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 b/PolyChat/MainPage.xaml index 5484d94..550dcc3 100644 --- a/PolyChat/MainPage.xaml +++ b/PolyChat/MainPage.xaml @@ -13,10 +13,11 @@ - + + @@ -37,7 +38,7 @@ - + @@ -53,14 +54,10 @@ - + - - - - - - + + @@ -97,13 +95,17 @@ - + + + + - - - - - + + + + diff --git a/PolyChat/MainPage.xaml.cs b/PolyChat/MainPage.xaml.cs index 42f1ca2..9c45126 100644 --- a/PolyChat/MainPage.xaml.cs +++ b/PolyChat/MainPage.xaml.cs @@ -1,12 +1,17 @@ -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 System.Text.Json; +using Windows.Foundation; +using Windows.UI.Core; 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 @@ -17,63 +22,90 @@ namespace PolyChat /// public sealed partial class MainPage : Page { - private NetworkingController networkingController; + private Controller Controller; private ObservableCollection Partners; private ChatPartner selectedPartner = null; private string username; + private static ElementTheme Theme = ElementTheme.Light; + public MainPage() { 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(); + // theming + RequestedTheme = Theme; + // updated placeholder updateNoChatsPlaceholder(); updateNoUsernamePlaceholder(); updateNoChatSelected(); updateSendButtonEnabled(); } - public async void ShowConnectionError(string message) + public async void ShowConnectionError(string param, string heading, string message) { - ConnectionFailedDialog dialog = new ConnectionFailedDialog(message); - var result = await dialog.ShowAsync(); - if (result == ContentDialogResult.Primary) + await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { - //retry - } - // else abort -> del chat + 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 */ } + ) + ); + SafelyOpenDialog(dialog); + }); } // 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); + selectedPartner.AddMessage(new ChatMessage(username, "message", inputSend.Text, DateTime.Now, false)); + Controller.SendMessage(selectedPartner.Code, "message", inputSend.Text); // clear input inputSend.Text = ""; } public async void OnOpenNewChatDialog(object sender = null, RoutedEventArgs e = null) { + // test + /* + OnIncomingMessage( + "localhost", + new JObject( + new JProperty("type", "message"), + new JProperty("content", "Test") + ).ToString(), + 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()); - networkingController.connectNewClient(ip); - Partners.Add(new ChatPartner( + string ip = IP.GetIPFromCode(dialog.getValue()); + Controller.Connect(ip); + ChatPartner pa = Partners.FirstOrDefault(p => p.Code == ip); + if (pa == null) + Partners.Add(new ChatPartner( "Connecting...", ip )); @@ -84,41 +116,133 @@ 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(); - if (username.Length == 0) textUsername.Text = "Unknown"; - else textUsername.Text = username; + textUsername.Text = username; + Controller.SendBroadcastMessage("username", username); } updateNoUsernamePlaceholder(); } - public void OnIncomingMessage(Message message) + /// + /// Adds a new ChatPartner to the UI with default Name. + /// + /// IP Adress, gets shown as Util.IP > Code + public async void OnIncomingConnection(string ip) { - ChatPartner sendingPartner = Partners.First(p => p.Code == message.Ip); - sendingPartner.AddMessage(new Message( - message.Msg, - true, - message.Sender - )); + await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + Partners.Add(new ChatPartner( + "Connecting...", + ip + )); + updateNoChatsPlaceholder(); + }); + } + /// + /// Adds an message to the UI, based on .sender if known + /// + /// ChatMessage + public async void OnIncomingMessage(string origin, string json, DateTime timeStamp) + { + 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); + switch (type) + { + case "username": + Debug.WriteLine($"! username change for {sendingPartner.Code} -> {content}"); + 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, true)); + break; + } + }); + } + 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()), + false // TODO: FIX !!!! + ) + ); + } + }); } private void OnDeleteChat(object sender = null, RoutedEventArgs e = null) { + Controller.CloseChat(selectedPartner.Code,delete: true); Partners.Remove(selectedPartner); updateNoChatsPlaceholder(); + updateNoChatSelected(); + } + + public async void OnChatPartnerDeleted(string code) + { + await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => + { + Partners.Remove(Partners.FirstOrDefault(p => p.Code.Equals(code))); + selectedPartner = null; + updateNoChatsPlaceholder(); + updateNoChatSelected(); + }); + } + public void OnChatPartnerSelected(object sender, RoutedEventArgs e) + { + string code = ((RadioButton)sender).Tag.ToString(); + selectedPartner = Partners.FirstOrDefault(p => p.Code == code); + listViewMessages.ItemsSource = selectedPartner.Messages; + selectedPartnerName.Text = selectedPartner.Name; + updateNoChatSelected(); + 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(); - if (e.Key == Windows.System.VirtualKey.Enter) + if (buttonSend.IsEnabled && e.Key == Windows.System.VirtualKey.Enter) { OnSendMessage(); } } + public static IAsyncOperation SafelyOpenDialog(ContentDialog d) + { + if(VisualTreeHelper.GetOpenPopups(Window.Current).Count == 0) + return d.ShowAsync(); + return null; + } + + // GETTERS + + public static ElementTheme GetTheme() => Theme; + // UPDATE FUNCTIONS FOR UI PLACEHOLDERS private void updateNoChatsPlaceholder() diff --git a/PolyChat/Models/ChatMessage.cs b/PolyChat/Models/ChatMessage.cs index 28cf47c..1f6d272 100644 --- a/PolyChat/Models/ChatMessage.cs +++ b/PolyChat/Models/ChatMessage.cs @@ -1,29 +1,42 @@ using System; +using System.Diagnostics; +using System.Text.Json; +using Windows.UI.Xaml; 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; - public readonly bool Foreign; + public string Origin; + public string Type; + public string Content; + public DateTime TimeStamp; + public HorizontalAlignment Align; + private bool Foreign; - public ChatMessage(string Content = "", bool Foreign = true, string Sender= "Unknown", string Ip = "127.0.0.1") + /// + /// 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) { - this.Sender = Sender; - this.Timestamp = DateTime.Now; - this.Content = Content; - this.Foreign = Foreign; - this.Ip = Ip; + Origin = origin; + TimeStamp = timeStamp; + Type = type; + Content = content; + Align = foreign ? HorizontalAlignment.Right : HorizontalAlignment.Left; + Foreign = foreign; + Debug.WriteLine("Created Loaded 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/Models/ChatPartner.cs b/PolyChat/Models/ChatPartner.cs index 8b93455..abc5f00 100644 --- a/PolyChat/Models/ChatPartner.cs +++ b/PolyChat/Models/ChatPartner.cs @@ -1,5 +1,8 @@ using SocketIOSharp.Server.Client; using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.CompilerServices; namespace PolyChat.Models { @@ -7,20 +10,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/Connection.cs b/PolyChat/Models/Connection.cs new file mode 100644 index 0000000..5d45440 --- /dev/null +++ b/PolyChat/Models/Connection.cs @@ -0,0 +1,101 @@ +using System; +using System.Diagnostics; +using EngineIOSharp.Common.Enum; +using Newtonsoft.Json.Linq; +using SocketIOSharp.Client; +using SocketIOSharp.Common; +using SocketIOSharp.Server.Client; +using PolyChat.Models; + +namespace PolyChat.Models +{ + public class Connection + { + private SocketIOClient Client; + private SocketIOSocket Socket; + private bool Connected = false; + private readonly string IP; + private Action DeleteConnection; + + 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(); + // 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(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); + + //we are connected if we got here, inital packet was already received + Connected = true; + } + 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("--- Sending initial packet to server ---"); + Client.Emit("initial", Controller.getIP()); + Debug.WriteLine("--- Connection successfull ---"); + Connected = true; + } + private void OnDisconnect() + { + Debug.WriteLine("--- Disconnected! ---"); + Debug.WriteLine($"--- Deleting Connection with IP: {IP}---"); + DeleteConnection(IP, IsConnected(),false); + 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("---"); + Close(); + } + + // Getters + + public void Close() + { + if (Client != null) Client.Close(); + if (Socket != null) Socket.Close(); + } + + public bool IsConnected() + { + return Connected; + } + + public string getIP() + { + return IP; + } + } +} 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/Models/FileManager.cs b/PolyChat/Models/FileManager.cs new file mode 100644 index 0000000..d0ca688 --- /dev/null +++ b/PolyChat/Models/FileManager.cs @@ -0,0 +1,258 @@ +using System; +using System.Collections.Generic; +using System.Linq; +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 Controller controller; + + + //=============================================================================================================================================== + //Constructor + //=============================================================================================================================================== + public FileManager(Controller controller) + { + this.controller = controller; + } + + //=============================================================================================================================================== + // editing save files + //=============================================================================================================================================== + + /// + /// 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+".txt")) + { + File.Delete(path); + return; + } + } + } + } + } + + /// + /// loads one chatlog probably when someone tries to connect + /// + /// + public void loadChat(String ip) + { + //load dir and create if non existant + if (Directory.Exists("U:\\PolyChat\\Saves")) + { + Debug.WriteLine("--Path exists.--"); + if (File.Exists($"U:\\PolyChat\\Saves\\{ip}.txt")) + { + String jsonArr = decrypt(File.ReadAllText($"U:\\PolyChat\\Saves\\{ip}.txt")); + controller.SendIncomingMessageUI(ip, jsonArr); + } + } + } + + /// + /// sends chatlogs as json array to uiController with 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); + controller.SendIncomingMessageUI(ip, jsonArr); + } + } + } + + /// + /// Saves incoming chat message to U:\PolyChat\Saves\ip.txt + /// + /// + /// + public void saveChats(string ip, string json, DateTime timeStamp) + { + new Thread(() => + { + //breaking if namechange + JObject obj = JObject.Parse(json); + if (!obj["type"].ToString().Equals("username")) + { + //adding timestamp + 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("]")) + { + //structure intact + //save new chat + 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)); //encrypt and save to textfile + } + else + { + //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} ]")); //encrypt and write to file + } + } + else + { + //setup file + Debug.WriteLine("--Creating new File--"); + File.WriteAllText($"U:\\PolyChat\\Saves\\{ip}.txt", encrypt($"[ {json} ]")); + } + } + }).Start(); + } + + + //=============================================================================================================================================== + // Encryption + //=============================================================================================================================================== + + /// + /// generates keypair [public, privte] (100% secure, trust me) + /// + /// + private String[] genKeys() + { + return new String[] {"12345678", "12345678" }; + } + + + /// + /// does exactly what it says. XD + /// + /// encrypts given string and returns unreadable string + /// + /// + /// + private String encrypt(String toEncrypt) + { + try + { + string textToEncrypt = toEncrypt; + string ToReturn = ""; + string publickey = genKeys()[0]; + string secretkey = genKeys()[1]; + 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); + } + } + + /// + /// does exactly what it says. XD + /// + /// takes in unreadable string and returns infirmation + /// + /// + /// + private String decrypt(String toDecrypt) + { + try + { + string textToDecrypt = toDecrypt; + string ToReturn = ""; + string publickey = genKeys()[0]; + string privatekey = genKeys()[1]; + 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/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/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/Package.appxmanifest b/PolyChat/Package.appxmanifest index 3d5e5b6..e3edf7e 100644 --- a/PolyChat/Package.appxmanifest +++ b/PolyChat/Package.appxmanifest @@ -13,8 +13,8 @@ + Publisher="CN=PatrickMarcFelix" + Version="0.1.0.0" /> @@ -87,4 +87,4 @@ - \ No newline at end of file + diff --git a/PolyChat/PolyChat.csproj b/PolyChat/PolyChat.csproj index 3410aba..df5e9f4 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 @@ -119,20 +127,21 @@ App.xaml + + + MainPage.xaml - + - - - + - - ConnectionFailedDialog.xaml + + Dialog.xaml EditUsernameDialog.xaml @@ -165,7 +174,7 @@ MSBuild:Compile Designer - + MSBuild:Compile Designer @@ -185,9 +194,15 @@ 6.2.12 + + 0.9.13 + 2.0.3 + + 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/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..f8b5bd6 --- /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 System.Threading; +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; + RequestedTheme = MainPage.GetTheme(); + } + + 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(); + } + } +} 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 600c62f..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() @@ -29,7 +30,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; diff --git a/screenshots/polychat_dark.PNG b/screenshots/polychat_dark.PNG new file mode 100644 index 0000000..3251733 Binary files /dev/null and b/screenshots/polychat_dark.PNG differ diff --git a/screenshots/polychat_light.PNG b/screenshots/polychat_light.PNG new file mode 100644 index 0000000..86c4e88 Binary files /dev/null and b/screenshots/polychat_light.PNG differ