private async void AddChatButton_OnClick(object sender, RoutedEventArgs e) { var dialog = new AddChatDialog(); string username = "", name = ""; byte delegateResult = 0; // 0 is running, 1 is done, 2 is canceled Exception error = null; await DialogHost.Show(dialog, "MainWindowDialogHost", async delegate(object sender1, DialogClosingEventArgs args) { if (!(bool)args.Parameter) { delegateResult = 2; // check if the dialog is canceled return; } username = dialog.IdTextBox.Text; // check if the ID exists in database var dbResult = await SharedStuff.Database.Table <DatabaseHelper.Users>().Where(user => user.Username == username) .ToArrayAsync(); if (dbResult.Length == 0) // get the user's key from server { NameValueCollection queryString = HttpUtility.ParseQueryString(string.Empty); queryString.Add("username", username); string url = SharedStuff.CreateUrlWithQuery( "https://" + Properties.Settings.Default.ServerAddress + "/users/getData", queryString); string result; try { using (WebClient wc = new WebClient()) // request other user's public key result = await wc.DownloadStringTaskAsync(url); } catch (Exception ex) { error = new Exception("Cannot connect to server", ex); delegateResult = 1; return; } // check if the username exists try { var data = JsonConvert.DeserializeObject <JsonTypes.UserDataStruct>(result); if (data.PublicKey == "") { throw new Exception("User is not logged in."); } string key = Convert.ToBase64String(SharedStuff.Curve.calculateAgreement( Convert.FromBase64String(_savedData.PrivateKey), Convert.FromBase64String(data.PublicKey))); await SharedStuff.Database.InsertAsync( new DatabaseHelper.Users // save the key in database { Username = data.Username, Name = data.Name, Key = key, }); name = data.Name; } catch (Exception) { var data = JsonConvert.DeserializeObject <JsonTypes.ServerStatus>(result); error = new Exception("Server returned an error", new Exception(data.Message)); } finally { delegateResult = 1; } } }); // wait until the results are gathered while (delegateResult == 0) { await Task.Delay(50); } if (delegateResult == 2) { return; } if (error != null) { var errDialog = new ErrorDialogSample { ErrorTitle = { Text = error.Message }, ErrorText = { Text = error.InnerException.Message } }; await DialogHost.Show(errDialog, "MainWindowDialogHost"); return; } // check if messages list does not contain this user if (MessagesList.All(x => x.Username != username)) { MessagesList.Insert(0, new MainMessagesNotify { FullDate = DateTime.Now, IsLastMessageForUser = false, Message = "", Name = name, Username = username }); } // open the chat page OpenWindowsList.Add(username, new ChatWindow(username)); OpenWindowsList[username].Show(); }
private async void OpenFileClicked(object sender, RoutedEventArgs e) { if (sender is Button btn) { if (btn.CommandParameter is string token) { DatabaseHelper.Files f = new DatabaseHelper.Files(); try { f = await SharedStuff.Database.Table <DatabaseHelper.Files>() .Where(file => file.Token == token).FirstAsync(); if (string.IsNullOrEmpty(f.Location)) { throw new InvalidOperationException(); } if (f.Location == " ") // null means the file is not downloaded. ' ' means it does not exists { throw new FileNotFoundException(); } if (File.Exists(f.Location)) // check if the downloaded file exists { System.Diagnostics.Process.Start(f.Location); } else { await SharedStuff.Database.ExecuteAsync( "UPDATE Files SET Location = ? WHERE Token = ?" , " ", token); throw new FileNotFoundException(); } } catch (FileNotFoundException) { // at first check if server has the file var err = new ErrorDialogSample { ErrorText = { Text = "This file does not exists on your computer. Ask other user to send this file again." }, ErrorTitle = { Text = "Cannot Open File" } }; await DialogHost.Show(err, "ChatDialogHost" + Username); } catch (InvalidOperationException) // this token does not exists in database { // download this file await Application.Current.Dispatcher.Invoke(async delegate { ProgressBar bar = ((StackPanel)((DockPanel)((Button)sender).Parent).Parent) .Children[1] as ProgressBar; try { string downloadFileUrl = $"https://{Properties.Settings.Default.ServerAddress}/download?token={token}"; // token does not contain special characters so we are good string destinationFilePath = Path.GetTempFileName(); using (var client = new HttpClientDownloadWithProgress(downloadFileUrl, destinationFilePath)) { client.ProgressChanged += (totalFileSize, totalBytesDownloaded, progressPercentage) => { bar.Value = progressPercentage ?? 0; }; await client.StartDownload(); } // get the key string key = (await SharedStuff.Database.Table <DatabaseHelper.Users>() .Where(user => user.Username == Username).FirstAsync()).Key; BouncyCastleHelper.AesGcmDecrypt(new FileInfo(destinationFilePath), new FileInfo(Path.Combine(SharedStuff.DownloadPath, f.Name)), key); await SharedStuff.Database.ExecuteAsync( "UPDATE Files SET Location = ? WHERE Token = ?" , Path.Combine(SharedStuff.DownloadPath, f.Name), token); } catch (FileNotFoundException) // This error means that the files does not exists on server. (it's too old) { await SharedStuff.Database.ExecuteAsync( "UPDATE Files SET Location = ? WHERE Token = ?" , " ", token); // this will make the File.Exists always return false MessagesList.First(msg => msg.Token == token).DownloadButtonIcon = PackIconKind.DownloadOff; var err = new ErrorDialogSample { ErrorText = { Text = "This file is too old and could not be downloaded. Ask other user to send this file again." }, ErrorTitle = { Text = "Cannot Download File" } }; await DialogHost.Show(err, "ChatDialogHost" + Username); } catch (Exception ex) { var err = new ErrorDialogSample { ErrorText = { Text = ex.Message }, ErrorTitle = { Text = "Cannot Download File" } }; await DialogHost.Show(err, "ChatDialogHost" + Username); } finally { bar.Value = 101; } }); } } } }
private async void LoginButtonClicked(object sender, RoutedEventArgs e) { LoadingDialogSample loadingDialog = new LoadingDialogSample // simple dialog with a spinning indicator { LoadingTextText = { Text = "Connecting to server..." } // set the text of the dialog }; Exception error = null; await DialogHost.Show(loadingDialog, "LoginDialogHost", delegate(object sender1, DialogOpenedEventArgs args) // show the dialog { string serverAddress = ServerUrlTxt.Text, username = UsernameTxt.Text, password = PasswordTxt.Password; bool trustEveryThing = TrustSslCheckBox.IsChecked.HasValue && TrustSslCheckBox.IsChecked.Value; Task.Factory.StartNew(async() => // connect to server from another thread { // let's see to trust all certificates or not if (trustEveryThing) { ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true; } else { ServicePointManager.ServerCertificateValidationCallback = null; } // at first get server's public RSA key try { if (SharedStuff.ServerPublicKey == null) { using (var wc = new WebClient()) { string key = wc.DownloadString("https://" + serverAddress + "/publicKey"); SharedStuff.ServerPublicKey = BouncyCastleHelper.ReadAsymmetricKeyParameter(key); } } } catch (Exception ex) { error = new Exception("Can't get public key", ex); // close dialog on main thread (because it is not running from main thread) await Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => args.Session.Close())); return; } // now login the user string toSave; try { // at first generate a ecdh curve25519 key pair var curveKeys = SharedStuff.Curve.generateKeyPair(); // encrypt the password with server's public key string rsaPassword = Convert.ToBase64String(BouncyCastleHelper.RsaEncrypt(SharedStuff.ServerPublicKey, password)); // build the request var postValues = new FormUrlEncodedContent(new[] { new KeyValuePair <string, string>("username", username), new KeyValuePair <string, string>("password", rsaPassword), new KeyValuePair <string, string>("key", Convert.ToBase64String(curveKeys.getPublicKey())), }); var result = await SharedStuff.Client.PostAsync("https://" + serverAddress + "/users/registerClient", postValues); string resultContent = await result.Content.ReadAsStringAsync(); // parse the server result var jsonRes = JsonConvert.DeserializeObject <JsonTypes.ServerStatus>(resultContent); if (!jsonRes.Ok) { throw new Exception("Server returned an error: " + jsonRes.Message); } // save the login details toSave = JsonConvert.SerializeObject(new JsonTypes.SavedData { Username = username, Password = password, PublicKey = Convert.ToBase64String(curveKeys.getPublicKey()), PrivateKey = Convert.ToBase64String(curveKeys.getPrivateKey()) }); Properties.Settings.Default.Name = jsonRes.Message; Properties.Settings.Default.ServerAddress = serverAddress; } catch (Exception ex) { error = new Exception("Can't login you", ex); // close dialog on main thread (because it is not running from main thread) await Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => args.Session.Close())); return; } // save the data (we had a successful login) try { byte[] encrypted = ProtectedData.Protect(Encoding.UTF8.GetBytes(toSave), null, DataProtectionScope.CurrentUser); // encrypt the data Properties.Settings.Default.LoginData = Convert.ToBase64String(encrypted); // save data to application settings Properties.Settings.Default.TrustInvalidSSL = trustEveryThing; Properties.Settings.Default.Save(); } catch (Exception ex) { error = new Exception("Cannot save data", ex); // TODO: logout the user if this fails // close dialog on main thread (because it is not running from main thread) await Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => args.Session.Close())); return; } // close dialog on main thread (because it is not running from main thread) await Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => args.Session.Close())); }); }); // check for errors if (error != null) { ErrorDialogSample errorDialog = new ErrorDialogSample { ErrorTitle = { Text = error.Message }, ErrorText = { Text = error.InnerException.Message } // Inner exception is never empty }; await DialogHost.Show(errorDialog, "LoginDialogHost"); } else // login the user { Hide(); new MainChatsWindow().Show(); } }