From ebc031f4f4b3ba708fe4851b42d351550298df0c Mon Sep 17 00:00:00 2001 From: rookiestyle Date: Sat, 21 Sep 2024 12:49:20 +0200 Subject: [PATCH] Improvements and hot key support for Linux/Unix Small improvements, especially regarding display of the KeePassOTP tab in the entry form. Allow hot key usage on Linux / Unix to auto-type OTP --- src/DAO/TFASites.cs | 58 ++++++++++++++++++++-------------- src/KPOTP_Details.cs | 19 +++++++---- src/KeePassOTPExt.cs | 15 ++++++++- src/Options.Designer.cs | 16 +++++++++- src/Options.cs | 15 +++++++++ src/Properties/AssemblyInfo.cs | 4 +-- version.info | 2 +- 7 files changed, 94 insertions(+), 35 deletions(-) diff --git a/src/DAO/TFASites.cs b/src/DAO/TFASites.cs index 707322c..bd9241f 100644 --- a/src/DAO/TFASites.cs +++ b/src/DAO/TFASites.cs @@ -15,7 +15,7 @@ namespace KeePassOTP public static class TFASites { //const string TFA_JSON_FILE = "https://twofactorauth.org/api/v2/tfa.json"; - const string TFA_JSON_FILE_DEFAULT = "https://2fa.directory/api/v3/tfa.json"; + const string TFA_JSON_FILE_DEFAULT = "https://api.2fa.directory/v3/tfa.json"; public static string TFA_JSON_FILE { @@ -254,27 +254,34 @@ private static void ReadOTPSites(object s) Dictionary dTFAEntries = new Dictionary(); foreach (string tfaentry in lTFAEntries) { - TFAData tfa = new TFAData(); - tfa.domain = GetJSonString(tfaentry, "domain"); - string sDomain = tfa.domain.ToLowerInvariant(); - if (!sDomain.StartsWith("http://") && !sDomain.StartsWith("https://")) tfa.domain = "https://" + tfa.domain; + try + { + TFAData tfa = new TFAData(); + tfa.domain = GetJSonString(tfaentry, "domain"); + string sDomain = tfa.domain.ToLowerInvariant(); + if (!sDomain.StartsWith("http://") && !sDomain.StartsWith("https://")) tfa.domain = "https://" + tfa.domain; - tfa.img = GetJSonString(tfaentry, "img"); - tfa.url = GetJSonString(tfaentry, "url"); - if (string.IsNullOrEmpty(tfa.url)) tfa.url = tfa.domain; - tfa.tfa = GetJSonList(tfaentry, "tfa"); - tfa.documentation = GetJSonString(tfaentry, "documentation"); - tfa.recovery = GetJSonString(tfaentry, "recovery"); - tfa.notes = GetJSonString(tfaentry, "notes"); - tfa.contact = GetJSonString(tfaentry, "contact"); - tfa.regions = GetJSonList(tfaentry, "regions"); - tfa.additional_domains = GetJSonList(tfaentry, "additional_domains"); - tfa.custom_software = GetJSonList(tfaentry, "custom_software"); - tfa.custom_hardware = GetJSonList(tfaentry, "custom_hardware"); - tfa.keywords = GetJSonList(tfaentry, "keywords"); - string sRegexPattern = CreatePattern(tfa.domain); - tfa.RegexUrl = new Regex(sRegexPattern); - m_dTFA[sRegexPattern] = tfa; + tfa.img = GetJSonString(tfaentry, "img"); + tfa.url = GetJSonString(tfaentry, "url"); + if (string.IsNullOrEmpty(tfa.url)) tfa.url = tfa.domain; + tfa.tfa = GetJSonList(tfaentry, "tfa"); + tfa.documentation = GetJSonString(tfaentry, "documentation"); + tfa.recovery = GetJSonString(tfaentry, "recovery"); + tfa.notes = GetJSonString(tfaentry, "notes"); + tfa.contact = GetJSonString(tfaentry, "contact"); + tfa.regions = GetJSonList(tfaentry, "regions"); + tfa.additional_domains = GetJSonList(tfaentry, "additional_domains"); + tfa.custom_software = GetJSonList(tfaentry, "custom_software"); + tfa.custom_hardware = GetJSonList(tfaentry, "custom_hardware"); + tfa.keywords = GetJSonList(tfaentry, "keywords"); + string sRegexPattern = CreatePattern(tfa.domain); + tfa.RegexUrl = new Regex(sRegexPattern); + m_dTFA[sRegexPattern] = tfa; + } + catch (Exception exAll) + { + PluginDebug.AddError("Error reading OTP sites", 0, exAll.Message, tfaentry); + } } DateTime dtEnd = DateTime.Now; lock (m_TFAReadLock) @@ -288,6 +295,7 @@ private static List ParseJSON(string content) { bool bRepeat = true; MatchCollection aMatches = null; + while (bRepeat) { try @@ -296,10 +304,14 @@ private static List ParseJSON(string content) aMatches = r.Matches(content); bRepeat = false; } - catch (Exception ex) { } + catch (Exception ex) { + PluginDebug.AddError("Error in ParseJSON", 0, ex.Message); + } } List lResult = new List(); - foreach (Match m in aMatches) lResult.Add(m.Value); + + lResult = aMatches.Cast().Select(m => m.Value).ToList(); + return lResult; } diff --git a/src/KPOTP_Details.cs b/src/KPOTP_Details.cs index 311fe4e..dc25967 100644 --- a/src/KPOTP_Details.cs +++ b/src/KPOTP_Details.cs @@ -40,7 +40,13 @@ internal void InitEx(PwEntryForm f) if (_tc == null) return; InitTab(); + _tc.Selected += OnTabSelected; + + DoRefresh(); + } + private void OnTabSelected(object sender, TabControlEventArgs e) + { DoRefresh(); } @@ -59,7 +65,6 @@ private void InitTab() _tpKeePassOTP.Controls.Add(this); if (_pef != null) { - _tc.TabPages[0].Leave += CheckActiveTabStop; _tc.TabPages.Insert(5, _tpKeePassOTP); } else @@ -78,6 +83,7 @@ private void DoRefresh() var tfa = TFASites.GetTFAData(_pef.EntryStrings.ReadSafe(PwDefs.UrlField)); DoRefresh(tfa); } + private void DoRefresh(TFASites.TFAData tfa) { if (tfa == null) @@ -94,15 +100,14 @@ private void DoRefresh(TFASites.TFAData tfa) this.Enabled = tfa.tfa_possible; - if (!tfa.tfa_possible) _tc.TabPages.Remove(_tpKeePassOTP); + if (!tfa.tfa_possible) + { + if (_tc.SelectedTab == _tpKeePassOTP) _tc.SelectTab(0); + if (_tc.TabPages.Contains(_tpKeePassOTP)) _tc.TabPages.Remove(_tpKeePassOTP); + } else if (!_tc.TabPages.Contains(_tpKeePassOTP)) _tc.TabPages.Insert(5, _tpKeePassOTP); } - private void CheckActiveTabStop(object sender, EventArgs e) - { - DoRefresh(); - } - private void SetupUrl(Label l, LinkLabel ll, string url) { ll.Text = string.Empty; diff --git a/src/KeePassOTPExt.cs b/src/KeePassOTPExt.cs index f5c0c3e..b12ad3e 100644 --- a/src/KeePassOTPExt.cs +++ b/src/KeePassOTPExt.cs @@ -26,7 +26,7 @@ public partial class KeePassOTPExt : Plugin { #region members private IPluginHost m_host = null; - + private const string m_sIpcEventName_KPOTP = "kpotp"; //menu stuff private ToolStripMenuItem m_ContextMenu; private ToolStripMenuItem m_ContextMenuCopy; @@ -98,9 +98,19 @@ public override bool Initialize(IPluginHost host) GlobalWindowManager.WindowAdded += GlobalWindowManager_WindowAdded; + IpcUtilEx.IpcEvent += OnIpcEvent; + return true; } + private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs) + { + if (ipcEventArgs.Name.Equals(m_sIpcEventName_KPOTP, StringComparison.InvariantCultureIgnoreCase)) + { + m_host.MainWindow.BeginInvoke(new Action(() => { PTHotKeyManager_HotKeyPressed(null, null); })); + } + } + private void CleanupColumns() { //Column KPOTP_Reduced has been removed (use KeePassOTP options instead) @@ -553,6 +563,7 @@ private void OptionsFormShown(object sender, Tools.OptionsFormsEventArgs e) PluginDebug.AddInfo("Options page prepared, " + dDB.Count.ToString() + " open databases found", 0); options.InitEx(dDB, m_host.Database); PluginDebug.AddInfo(dDB.Count.ToString() + " open databases added to options page", 0); + e.form.Shown += options.OptionsFormShown; Tools.AddPluginToOptionsForm(this, options); } @@ -1176,6 +1187,8 @@ public override void Terminate() RemoveTray(); RemoveMenu(); + IpcUtilEx.IpcEvent -= OnIpcEvent; + PluginDebug.SaveOrShow(); m_host = null; diff --git a/src/Options.Designer.cs b/src/Options.Designer.cs index 890854e..8600c7a 100644 --- a/src/Options.Designer.cs +++ b/src/Options.Designer.cs @@ -68,6 +68,7 @@ private void InitializeComponent() this.tpHelp = new System.Windows.Forms.TabPage(); this.tbHelp = new System.Windows.Forms.TextBox(); this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.llHotKeyUnix = new System.Windows.Forms.LinkLabel(); this.tabControl1.SuspendLayout(); this.tpGeneral.SuspendLayout(); this.gOtherOptions.SuspendLayout(); @@ -214,6 +215,7 @@ private void InitializeComponent() // // pPlaceholder // + this.pPlaceholder.Controls.Add(this.llHotKeyUnix); this.pPlaceholder.Controls.Add(this.cbAutoSubmit); this.pPlaceholder.Controls.Add(this.tbPlaceholder); this.pPlaceholder.Controls.Add(this.lPlaceholder); @@ -552,6 +554,17 @@ private void InitializeComponent() this.tbHelp.Size = new System.Drawing.Size(1331, 586); this.tbHelp.TabIndex = 0; // + // llHotKeyUnix + // + this.llHotKeyUnix.AutoSize = true; + this.llHotKeyUnix.Location = new System.Drawing.Point(0, 48); + this.llHotKeyUnix.Name = "llHotKeyUnix"; + this.llHotKeyUnix.Size = new System.Drawing.Size(179, 32); + this.llHotKeyUnix.TabIndex = 141; + this.llHotKeyUnix.TabStop = true; + this.llHotKeyUnix.Text = "llHotKeyUnix"; + this.llHotKeyUnix.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.llHotKeyUnix_LinkClicked); + // // Options // this.AutoScaleDimensions = new System.Drawing.SizeF(16F, 31F); @@ -629,5 +642,6 @@ private void InitializeComponent() internal System.Windows.Forms.CheckBox cbCheckTFA; private System.Windows.Forms.ComboBox cbOTPDisplay; internal System.Windows.Forms.CheckBox cbLocalHotkey; - } + private System.Windows.Forms.LinkLabel llHotKeyUnix; + } } \ No newline at end of file diff --git a/src/Options.cs b/src/Options.cs index a37da6c..c751d80 100644 --- a/src/Options.cs +++ b/src/Options.cs @@ -517,5 +517,20 @@ private void tbPlaceholder_TextChanged(object sender, EventArgs e) cbAutoSubmit.Enabled = tbPlaceholder.Text.StartsWith("{") && tbPlaceholder.Text.EndsWith("}"); cbAutoSubmit.Text = string.Format(PluginTranslate.PlaceholderAutoSubmit, tbPlaceholder.Text); } + + internal void OptionsFormShown(object sender, EventArgs e) + { + llHotKeyUnix.Visible = KeePassLib.Native.NativeLib.IsUnix(); + llHotKeyUnix.Links.Clear(); + Control m_linkHotKeyHelp = Tools.GetControl("m_linkHotKeyHelp", sender as Form); + string sText = m_linkHotKeyHelp != null ? m_linkHotKeyHelp.Text : "Create system-wide hot keys"; + llHotKeyUnix.Links.Add(0, sText.Length); + llHotKeyUnix.Text = sText; + } + + private void llHotKeyUnix_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + Tools.OpenUrl("https://github.com/Rookiestyle/KeePassOTP/wiki/Unix-Linux-%E2%80%90-Create-system%E2%80%90wide-hot-keys"); + } } } diff --git a/src/Properties/AssemblyInfo.cs b/src/Properties/AssemblyInfo.cs index 4027cd2..1a0d8b8 100644 --- a/src/Properties/AssemblyInfo.cs +++ b/src/Properties/AssemblyInfo.cs @@ -30,5 +30,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.7.2")] -[assembly: AssemblyFileVersion("1.7.2")] +[assembly: AssemblyVersion("1.8")] +[assembly: AssemblyFileVersion("1.8")] diff --git a/version.info b/version.info index 28f295e..bf73655 100644 --- a/version.info +++ b/version.info @@ -1,5 +1,5 @@ : -KeePassOTP:1.7.2 +KeePassOTP:1.8 KeePassOTP!de:25 KeePassOTP!fr:7 KeePassOTP!nl:3