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();
            }
        }