#region Disclaimer / License // Copyright (C) 2011, Kenneth Skovhede // http://www.hexad.dk, opensource@hexad.dk // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #endregion using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Text; using System.Windows.Forms; namespace Duplicati.Winforms.Controls { /// /// This class implements the logic for a control that displays a hidden password. /// It allows the user to toggle the password visibility, to prevent typing errors, /// and can also prevent the underlying password from being revealed, while still /// allowing the user to enter a new password. /// public partial class PasswordControl : UserControl { /// /// The internal password storage /// private string m_password = ""; /// /// A control field used to distinguish the code generated events from the user generated events /// private bool m_isUpdating = false; /// /// A variable that controls if the user is asked to clear the password /// private bool m_askToEnterNewPassword = false; /// /// The original password assigned to the control /// private string m_initialPassword = null; /// /// An event that is raised when the password changed /// [Browsable(true)] public new event EventHandler TextChanged; /// /// Simple delegate with no arguments, used to invoke the update function /// private delegate void EmptyDelegate(); /// /// Constructs a new PasswordControl /// public PasswordControl() { InitializeComponent(); } /// /// The password that this control protects /// public override string Text { //Mono does not play nice if Text returns null get { return m_password ?? ""; } set { if (value == null) value = ""; bool changed = m_password != value; m_password = value; UpdateDisplay(); if (changed && TextChanged != null) TextChanged(this, new EventArgs()); } } /// /// Gets or sets the initial password, used to check if the password was changed /// public string InitialPassword { get { return m_initialPassword; } set { m_initialPassword = value; } } /// /// A value that determines if the user can only clear the password, /// and not reveal the original password /// public bool AskToEnterNewPassword { get { return m_askToEnterNewPassword; } set { m_askToEnterNewPassword = value; UpdateDisplay(); } } /// /// Gets or sets a value that indicates if the password is visible to the user /// public bool IsPasswordVisible { get { return ShowPassword.Checked; } set { ShowPassword.Checked = value; } } /// /// Internal helper function that updates the user interface /// private void UpdateDisplay() { try { m_isUpdating = true; TextBox.UseSystemPasswordChar = !ShowPassword.Checked; if (TextBox.Focused) TextBox.Text = m_askToEnterNewPassword ? "" : m_password; else TextBox.Text = m_askToEnterNewPassword ? "password" : m_password; TextBox.SelectionStart = TextBox.Text.Length; TextBox.SelectionLength = 0; } finally { m_isUpdating = false; } } /// /// Event handler for the TextChanged event /// /// Unused sender argument /// Unused event argument private void TextBox_TextChanged(object sender, EventArgs e) { if (m_isUpdating) return; if (m_askToEnterNewPassword) { if (MessageBox.Show(this, Strings.PasswordControl.ConfirmClearingPassword, Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != DialogResult.Yes) { try { m_isUpdating = true; TextBox.Text = ""; BeginInvoke(new EmptyDelegate(OutOfOrderFocus)); return; } finally { m_isUpdating = false; } } } m_password = TextBox.Text; m_askToEnterNewPassword = false; if (TextChanged != null) TextChanged(this, e); } /// /// A helper for providing out-of-order focusing /// private void OutOfOrderFocus() { try { ShowPassword.Focus(); } catch { } } /// /// Event handler for the CheckedChanged event /// /// Unused sender argument /// Unused event argument private void ShowPassword_CheckedChanged(object sender, EventArgs e) { if (ShowPassword.Checked && m_askToEnterNewPassword) { if (MessageBox.Show(this, Strings.PasswordControl.ConfirmClearingPassword, Application.ProductName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) != DialogResult.Yes) { ShowPassword.Checked = false; BeginInvoke(new EmptyDelegate(OutOfOrderFocus)); return; } else { m_password = ""; m_askToEnterNewPassword = false; } } UpdateDisplay(); } /// /// Event handler for the Enter event /// /// Unused sender argument /// Unused event argument private void TextBox_Enter(object sender, EventArgs e) { this.BeginInvoke(new EmptyDelegate(UpdateDisplay)); } /// /// Event handler for the Leave event /// /// Unused sender argument /// Unused event argument private void TextBox_Leave(object sender, EventArgs e) { this.BeginInvoke(new EmptyDelegate(UpdateDisplay)); } /// /// Event handler for the checkbox click event /// /// Unused sender argument /// Unused event argument private void ShowPassword_Click(object sender, EventArgs e) { try { TextBox.Focus(); } catch { } } /// /// A helper method that asks the user to verify the password if it was changed and is not show /// /// True if the password was verified, false otherwise public bool VerifyPasswordIfChanged() { if (!ShowPassword.Checked && (string.IsNullOrEmpty(m_initialPassword) != string.IsNullOrEmpty(m_password) || m_initialPassword != m_password)) { PasswordConfirmationDialog dlg = new PasswordConfirmationDialog(m_password); if (dlg.ShowDialog(this) == DialogResult.OK) { m_initialPassword = m_password; return true; } else return false; } else return true; } } }