Newer
Older
AutoTypeSearch / AutoTypeSearch / AutoTypeSearchExt.cs
using System;
using System.Linq;
using System.Windows.Forms;
using AutoTypeSearch.Properties;
using KeePass;
using KeePass.Forms;
using KeePass.Plugins;
using KeePass.UI;
using KeePass.Util;
using KeePassLib;
using KeePassLib.Security;

namespace AutoTypeSearch
{
// ReSharper disable once ClassNeverInstantiated.Global - Plugin instantiated by KeePass
	public sealed class AutoTypeSearchExt : Plugin
    {
		private const string IpcEventName = "AutoTypeSearch";
		private const int UnixAutoTypeWaitTime = 500; // Milliseconds
		internal const string TagsVirtualFieldName = "***TAGS***";

		private IPluginHost mHost;
		private bool mAutoTypeSuccessful;
		private string mLastAutoTypeWindowTitle;

		public override string UpdateUrl
		{
			get { return "sourceforge-version://AutoTypeSearch/autotypesearch?-v(%5B%5Cd.%5D%2B)%5C.zip"; }
		}

		public override bool Initialize(IPluginHost host)
		{
			mHost = host;

			IpcUtilEx.IpcEvent += OnIpcEvent;
			GlobalWindowManager.WindowAdded += OnWindowAdded;
			if (!KeePassLib.Native.NativeLib.IsUnix())
			{
				HotKeyManager.HotKeyPressed += HotKeyManager_HotKeyPressed;
			}
			AutoType.SequenceQueriesEnd += OnAutoTypeSequenceQueriesEnd;

			Options.LoadSettings(host);

			return true;
		}

		#region Unsuccessful AutoType Detection
		private void OnAutoTypeSequenceQueriesEnd(object sender, SequenceQueriesEventArgs e)
		{
			// An auto-type has completed. Was it successful? Watch for an auto-type event, and for the UI thread unblocking. If the UI thread unblocks before the auto-type event, it wasn't successful.
			// (hacky, yes, but no other means possible to detect failed auto-types at the time of writing)

			if (Settings.Default.ShowOnFailedAutoType)
			{
				mAutoTypeSuccessful = false;
				mLastAutoTypeWindowTitle = e.TargetWindowTitle;
				AutoType.FilterCompilePre += OnAutoType;

				if (KeePassLib.Native.NativeLib.IsUnix())
				{
					// If Unix, can't rely on waiting for UI thread unblocking as the XDoTool mechanism calls DoEvents (in NativeMethods.TryXDoTool) before anything else.
					// Instead, just wait half a second and hope for the best.
					var timer = new Timer { Interval = UnixAutoTypeWaitTime };
					timer.Tick += delegate
					{
						timer.Stop();
						timer.Dispose();
						OnAutoTypeEnd();
					};
					timer.Start();
				}
				else
				{
					mHost.MainWindow.BeginInvoke((Action)OnAutoTypeEnd);
				}
			}
		}

		private void OnAutoType(object sender, AutoTypeEventArgs autoTypeEventArgs)
		{
			// Detach event, we are only interested in a single invocation.
			AutoType.FilterCompilePre -= OnAutoType;

			mAutoTypeSuccessful = true;
		}

		private void OnAutoTypeEnd()
		{
			// Detach event, the auto-type failed, it won't be received now.
			AutoType.FilterCompilePre -= OnAutoType;

			if (!mAutoTypeSuccessful)
			{
				ShowSearch(String.Format(Resources.AutoTypeFailedMessage, mLastAutoTypeWindowTitle));
			}
		}
		#endregion

		#region Options
		private void OnWindowAdded(object sender, GwmWindowEventArgs e)
		{
			var optionsForm = e.Form as OptionsForm;
			if (optionsForm != null)
			{
				Options.AddToWindow(optionsForm);
				return;
			}

			if (Settings.Default.ShowOnFailedAutoType)
			{
				var autoTypeCtxForm = e.Form as AutoTypeCtxForm;
				if (autoTypeCtxForm != null)
				{
					mAutoTypeSuccessful = true; // Don't show the search if the picker box is shown
					autoTypeCtxForm.Closed += OnAutoTypeCtxFormClosed;
				}
			}
		}

		private void OnAutoTypeCtxFormClosed(object sender, EventArgs e)
		{
			var autoTypeCtxForm = (AutoTypeCtxForm)sender;
			autoTypeCtxForm.Closed -= OnAutoTypeCtxFormClosed;

			if (autoTypeCtxForm.DialogResult == DialogResult.Cancel)
			{
				ShowSearch();
			}
		}
		#endregion

		public override void Terminate()
		{
			IpcUtilEx.IpcEvent -= OnIpcEvent;
			GlobalWindowManager.WindowAdded -= OnWindowAdded;

			if (!KeePassLib.Native.NativeLib.IsUnix())
			{
				HotKeyManager.HotKeyPressed -= HotKeyManager_HotKeyPressed;
				Options.UnregisterHotKey();
			}

			Options.SaveSettings(mHost);
			
			base.Terminate();
		}

		#region Search Initiation
		private void HotKeyManager_HotKeyPressed(object sender, HotKeyEventArgs e)
		{
			/*
			var testGroup = mHost.Database.RootGroup.FindCreateGroup("Test", true);
			for (int i = 0; i < 10000; i++)
			{
				var pwEntry = new PwEntry(true, true);
				pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(false, "Title " + i));
				pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(false, "User " + i));
				pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(false, "http://website/" + i));
				pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(false, "Notes " + i + "\nLine 2\n\nLine 3\nLine 4\nLine 5\n Line 6\n Line 7\nLine 8\nLine 9\nLine 10"));
				testGroup.AddEntry(pwEntry, true);
			}*/

			ShowSearch();
		}

		private void OnIpcEvent(object sender, IpcEventArgs ipcEventArgs)
		{
			if (Settings.Default.ShowOnIPC && ipcEventArgs.Name.Equals(IpcEventName, StringComparison.InvariantCultureIgnoreCase))
			{
				mHost.MainWindow.BeginInvoke(new Action(ShowSearch));
			}
		}

		private void ShowSearch()
		{
			ShowSearch(null);
		}

		private void ShowSearch(string infoText)
		{
			// Unlock, if required
			mHost.MainWindow.ProcessAppMessage((IntPtr)Program.AppMessage.Unlock, IntPtr.Zero);


			if (mHost.MainWindow.IsAtLeastOneFileOpen())
			{
				var searchWindow = new SearchWindow(mHost.MainWindow, infoText);
				searchWindow.Show();
				searchWindow.Activate();
			}
		}
		#endregion
	}
}