1 /*
2   KeePass Password Safe - The Open-Source Password Manager
3   Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
4 
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19 
20 using System;
21 using System.Collections.Generic;
22 using System.Diagnostics;
23 using System.Drawing;
24 using System.IO;
25 using System.Text;
26 using System.Text.RegularExpressions;
27 using System.Threading;
28 using System.Windows.Forms;
29 using System.Xml;
30 using System.Xml.Serialization;
31 
32 using KeePass.App;
33 using KeePass.App.Configuration;
34 using KeePass.DataExchange;
35 using KeePass.Ecas;
36 using KeePass.Native;
37 using KeePass.Plugins;
38 using KeePass.Resources;
39 using KeePass.UI;
40 using KeePass.Util;
41 using KeePass.Util.Archive;
42 using KeePass.Util.MultipleValues;
43 using KeePass.Util.Spr;
44 
45 using KeePassLib;
46 using KeePassLib.Collections;
47 using KeePassLib.Cryptography;
48 using KeePassLib.Cryptography.PasswordGenerator;
49 using KeePassLib.Delegates;
50 using KeePassLib.Interfaces;
51 using KeePassLib.Keys;
52 using KeePassLib.Security;
53 using KeePassLib.Serialization;
54 using KeePassLib.Utility;
55 
56 using NativeLib = KeePassLib.Native.NativeLib;
57 
58 namespace KeePass.Forms
59 {
60 	public partial class MainForm : Form
61 	{
62 		private DocumentManagerEx m_docMgr = new DocumentManagerEx();
63 
64 		private ListViewGroup m_lvgLastEntryGroup = null;
65 		private bool m_bEntryGrouping = false;
66 		private DateTime m_dtCachedNow = DateTime.UtcNow;
67 		private bool m_bOnlyTans = false;
68 		private string m_strLastEntryViewRtf = null;
69 		private Font m_fontExpired = null;
70 		private Font m_fontBoldUI = null;
71 		private Font m_fontBoldTree = null;
72 		private Font m_fontItalicTree = null;
73 		private Color m_clrAlternateItemBgColor = Color.Empty;
74 		private Point m_ptLastEntriesMouseClick = new Point(0, 0);
75 		private RichTextBoxContextMenu m_ctxEntryPreviewContextMenu = new RichTextBoxContextMenu();
76 		private DynamicMenu m_dynStringsMenu;
77 		private DynamicMenu m_dynStringsCtx;
78 		private DynamicMenu m_dynBinariesMenu;
79 		private DynamicMenu m_dynBinariesCtx;
80 		private DynamicMenu m_dynFindTagsMenu;
81 		private DynamicMenu m_dynFindTagsToolBar;
82 		private DynamicMenu m_dynTagAddMenu;
83 		private DynamicMenu m_dynTagAddCtx;
84 		private DynamicMenu m_dynTagRemoveMenu;
85 		private DynamicMenu m_dynTagRemoveCtx;
86 		private DynamicMenu m_dynMoveToGroupMenu;
87 		private DynamicMenu m_dynMoveToGroupCtx;
88 		private DynamicMenu m_dynAutoTypeAdvMenu;
89 		private DynamicMenu m_dynAutoTypeAdvCtx;
90 		private OpenWithMenu m_dynOpenUrlMenu;
91 		private OpenWithMenu m_dynOpenUrlCtx;
92 		private OpenWithMenu m_dynOpenUrlToolBar;
93 		private MenuItemLinks m_milMain = new MenuItemLinks();
94 
95 		private readonly AsyncPwListUpdate m_asyncListUpdate;
96 
97 		private MruList m_mruList = new MruList();
98 
99 		private SessionLockNotifier m_sessionLockNotifier = new SessionLockNotifier();
100 
101 		private DefaultPluginHost m_pluginDefaultHost = new DefaultPluginHost();
102 		private PluginManager m_pluginManager = new PluginManager();
103 
104 		private CriticalSectionEx m_csLockTimer = new CriticalSectionEx();
105 		private int m_nLockTimerMax = 0;
106 		// private volatile int m_nLockTimerCur = 0;
107 		private long m_lLockAtTicks = long.MaxValue;
108 		private uint m_uLastInputTime = uint.MaxValue;
109 		private long m_lLockAtGlobalTicks = long.MaxValue;
110 
111 		private uint m_uBlockQuickFind = 0;
112 		private object m_objQuickFindSync = new object();
113 		private int m_iLastQuickFindTicks = Environment.TickCount - 1500;
114 		private string m_strLastQuickSearch = string.Empty;
115 
116 		private ToolStripSeparator m_tsSepCustomToolBar = null;
117 		private List<ToolStripButton> m_vCustomToolBarButtons = new List<ToolStripButton>();
118 
119 		private int m_nClipClearMax = 0;
120 		private int m_nClipClearCur = -1;
121 
122 		private readonly string m_strNeverExpires = KPRes.NeverExpires;
123 		private readonly string m_strStatusReady = KPRes.Ready;
124 		private readonly string m_strNoneP = "(" + KPRes.None + ")";
125 
126 		private bool m_bSimpleTanView = true;
127 		private bool m_bShowTanIndices = true;
128 
129 		private Image m_imgFileSaveEnabled = null;
130 		private Image m_imgFileSaveDisabled = null;
131 		// private Image m_imgFileSaveAllEnabled = null;
132 		// private Image m_imgFileSaveAllDisabled = null;
133 		private List<Image> m_lStdClientImages = null;
134 		private ImageList m_ilCurrentIcons = null;
135 
136 		// private KeyValuePair<Color, Icon> m_kvpIcoMain =
137 		//	new KeyValuePair<Color, Icon>(Color.Empty, null);
138 		// private KeyValuePair<Color, Icon> m_kvpIcoTrayNormal =
139 		//	new KeyValuePair<Color, Icon>(Color.Empty, null);
140 		// private KeyValuePair<Color, Icon> m_kvpIcoTrayLocked =
141 		//	new KeyValuePair<Color, Icon>(Color.Empty, null);
142 
143 		private List<Image> m_lTabImages = new List<Image>();
144 		private ImageList m_ilTabImages = null;
145 
146 		private bool m_bIsAutoTyping = false;
147 		private uint m_uTabChangeBlocked = 0;
148 		private uint m_uForceSave = 0;
149 		private uint m_uUIBlocked = 0;
150 		private uint m_uUnlockAutoBlocked = 0;
151 		private MouseButtons m_mbLastTrayMouseButtons = MouseButtons.None;
152 		private uint m_uWindowStateAutoBlocked = 0;
153 		private uint m_uMainTimerBlocked = 0;
154 
155 		private bool m_bUpdateUIStateOnce = false;
156 		private int m_nLastSelChUpdateUIStateTicks = 0;
157 
158 		private readonly int m_nAppMessage = Program.ApplicationMessage;
159 		private readonly int m_nTaskbarButtonMessage;
160 		private bool m_bTaskbarButtonMessage;
161 
162 		private FormWindowState m_fwsLast = FormWindowState.Normal;
163 		private Control m_cLastActive = null;
164 		private PwGroup m_pgActiveAtDragStart = null;
165 		private Keys m_kLastUnhandledGroupsKey = Keys.None;
166 		private Stack<ShowWarningsLogger> m_sCancellable = new Stack<ShowWarningsLogger>();
167 
168 		// private Stack<Form> m_vRedirectActivation = new Stack<Form>();
169 
170 		private Size? m_oszClientLast = null;
171 		internal Size LastClientSize
172 		{
173 			get
174 			{
175 				if(m_oszClientLast.HasValue) return m_oszClientLast.Value;
176 				return this.ClientSize;
177 			}
178 		}
179 
180 		public DocumentManagerEx DocumentManager { get { return m_docMgr; } }
181 		public PwDatabase ActiveDatabase { get { return m_docMgr.ActiveDatabase; } }
182 		public ImageList ClientIcons { get { return m_ilCurrentIcons; } }
183 
184 		/// <summary>
185 		/// Get a reference to the main menu.
186 		/// </summary>
187 		public MenuStrip MainMenu { get { return m_menuMain; } }
188 		/// <summary>
189 		/// Get a reference to the 'Tools' popup menu in the main menu. It is
190 		/// recommended that you use this reference instead of searching the
191 		/// main menu for the 'Tools' item.
192 		/// </summary>
193 		public ToolStripMenuItem ToolsMenu { get { return m_menuTools; } }
194 
195 		public ContextMenuStrip EntryContextMenu { get { return m_ctxPwList; } }
196 		public ContextMenuStrip GroupContextMenu { get { return m_ctxGroupList; } }
197 		public ContextMenuStrip TrayContextMenu { get { return m_ctxTray; } }
198 
199 		public ToolStripProgressBar MainProgressBar { get { return m_statusPartProgress; } }
200 		public NotifyIcon MainNotifyIcon { get { return m_ntfTray.NotifyIcon; } }
201 
202 		public MruList FileMruList { get { return m_mruList; } }
203 
204 		internal PluginManager PluginManager { get { return m_pluginManager; } }
205 		internal bool HasFormLoaded { get { return m_bFormLoaded; } }
206 
207 		internal sealed class MainAppState
208 		{
209 			public bool FileLocked;
210 			public bool DatabaseOpened;
211 			public int EntriesCount;
212 			public int EntriesSelected;
213 			public bool EnableLockCmd;
214 			public bool NoWindowShown;
215 			public PwEntry SelectedEntry;
216 			public bool CanCopyUserName = false;
217 			public bool CanCopyPassword = false;
218 			public bool CanOpenUrl = false;
219 			public bool CanPerformAutoType = false;
220 			public bool IsOneTan = false;
221 			public string LockUnlock;
222 		}
223 
224 		internal enum AppCommandType
225 		{
226 			Window = 0,
227 			Lock = 1
228 		}
229 
230 		/// <summary>
231 		/// Check if the main window is trayed (i.e. only the tray icon is visible).
232 		/// </summary>
233 		/// <returns>Returns <c>true</c>, if the window is trayed.</returns>
IsTrayed()234 		public bool IsTrayed()
235 		{
236 			return !this.Visible;
237 		}
238 
IsFileLocked(PwDocument ds)239 		public bool IsFileLocked(PwDocument ds)
240 		{
241 			if(ds == null) ds = m_docMgr.ActiveDocument;
242 
243 			return (ds.LockedIoc.Path.Length != 0);
244 		}
245 
IsAtLeastOneFileOpen()246 		public bool IsAtLeastOneFileOpen()
247 		{
248 			foreach(PwDocument ds in m_docMgr.Documents)
249 			{
250 				if(ds.Database.IsOpen) return true;
251 			}
252 
253 			return false;
254 		}
255 
CleanUpEx()256 		private void CleanUpEx()
257 		{
258 			m_asyncListUpdate.CancelPendingUpdatesAsync();
259 
260 			Program.TriggerSystem.RaiseEvent(EcasEventIDs.AppExit);
261 
262 			MonoWorkarounds.Release(this);
263 			GlobalWindowManager.CustomizeFormHandleCreated(this, false, false);
264 
265 			m_nClipClearCur = -1;
266 			if(Program.Config.Security.ClipboardClearOnExit)
267 				ClipboardUtil.ClearIfOwner();
268 
269 			m_pluginManager.UnloadAllPlugins(); // Before saving the configuration
270 
271 			// Just unregister the events; no need to remove the buttons
272 			foreach(ToolStripButton tbCustom in m_vCustomToolBarButtons)
273 				tbCustom.Click -= OnCustomToolBarButtonClicked;
274 			m_vCustomToolBarButtons.Clear();
275 
276 			SaveConfig(); // After unloading plugins
277 
278 			m_sessionLockNotifier.Uninstall();
279 			HotKeyManager.UnregisterAll();
280 
281 			EntryTemplates.Release();
282 
283 			m_dynStringsMenu.MenuClick -= this.OnCopyCustomString;
284 			m_dynStringsCtx.MenuClick -= this.OnCopyCustomString;
285 			m_dynBinariesMenu.MenuClick -= this.OnEntryBinaryOpen;
286 			m_dynBinariesCtx.MenuClick -= this.OnEntryBinaryOpen;
287 			m_dynFindTagsMenu.MenuClick -= this.OnShowEntriesByTag;
288 			m_dynFindTagsToolBar.MenuClick -= this.OnShowEntriesByTag;
289 			m_dynTagAddMenu.MenuClick -= this.OnAddEntryTag;
290 			m_dynTagAddCtx.MenuClick -= this.OnAddEntryTag;
291 			m_dynTagRemoveMenu.MenuClick -= this.OnRemoveEntryTag;
292 			m_dynTagRemoveCtx.MenuClick -= this.OnRemoveEntryTag;
293 			m_dynMoveToGroupMenu.MenuClick -= this.OnEntryMoveToGroup;
294 			m_dynMoveToGroupCtx.MenuClick -= this.OnEntryMoveToGroup;
295 			m_dynAutoTypeAdvMenu.MenuClick -= this.OnEntryPerformAutoTypeAdv;
296 			m_dynAutoTypeAdvCtx.MenuClick -= this.OnEntryPerformAutoTypeAdv;
297 			m_dynOpenUrlMenu.Destroy();
298 			m_dynOpenUrlCtx.Destroy();
299 			m_dynOpenUrlToolBar.Destroy();
300 
301 			m_ctxEntryPreviewContextMenu.Detach();
302 
303 			m_lvsmMenu.Release();
304 			m_lvgmMenu.Release();
305 
306 			m_mruList.Release();
307 
308 			m_ntfTray.Visible = false;
309 
310 			// Debug.Assert(m_vRedirectActivation.Count == 0);
311 			Debug.Assert(m_uUIBlocked == 0);
312 			Debug.Assert(m_uUnlockAutoBlocked == 0);
313 			Debug.Assert(m_sCancellable.Count == 0);
314 			this.Visible = false;
315 
316 			if(m_fontBoldUI != null) { m_fontBoldUI.Dispose(); m_fontBoldUI = null; }
317 			if(m_fontBoldTree != null) { m_fontBoldTree.Dispose(); m_fontBoldTree = null; }
318 			if(m_fontItalicTree != null) { m_fontItalicTree.Dispose(); m_fontItalicTree = null; }
319 
320 			m_asyncListUpdate.WaitAll();
321 			m_bCleanedUp = true;
322 		}
323 
324 		/// <summary>
325 		/// Save the current configuration. The configuration is saved using the
326 		/// cascading configuration files mechanism and the default paths are used.
327 		/// </summary>
SaveConfig()328 		public void SaveConfig()
329 		{
330 			SaveWindowPositionAndSize();
331 
332 			AceMainWindow mw = Program.Config.MainWindow;
333 
334 			mw.Layout = ((m_splitHorizontal.Orientation != Orientation.Horizontal) ?
335 				AceMainWindowLayout.SideBySide : AceMainWindowLayout.Default);
336 
337 			UpdateColumnsEx(true);
338 
339 			Debug.Assert(m_bSimpleTanView == m_menuViewTanSimpleList.Checked);
340 			mw.TanView.UseSimpleView = m_bSimpleTanView;
341 			Debug.Assert(m_bShowTanIndices == m_menuViewTanIndices.Checked);
342 			mw.TanView.ShowIndices = m_bShowTanIndices;
343 
344 			Program.Config.MainWindow.ListSorting = m_pListSorter;
345 
346 			SerializeMruList(true);
347 
348 			// mw.ShowGridLines = m_lvEntries.GridLines;
349 
350 			AppConfigSerializer.Save(Program.Config);
351 		}
352 
SaveWindowPositionAndSize()353 		private void SaveWindowPositionAndSize()
354 		{
355 			if(!m_bFormLoadCalled) { Debug.Assert(false); return; }
356 
357 			FormWindowState ws = this.WindowState;
358 
359 			if(ws == FormWindowState.Normal)
360 			{
361 				Program.Config.MainWindow.X = this.Location.X;
362 				Program.Config.MainWindow.Y = this.Location.Y;
363 				Program.Config.MainWindow.Width = this.Size.Width;
364 				Program.Config.MainWindow.Height = this.Size.Height;
365 			}
366 
367 			if((ws == FormWindowState.Normal) || (ws == FormWindowState.Maximized))
368 			{
369 				Program.Config.MainWindow.SplitterHorizontalFrac =
370 					m_splitHorizontal.SplitterDistanceFrac;
371 				Program.Config.MainWindow.SplitterVerticalFrac =
372 					m_splitVertical.SplitterDistanceFrac;
373 			}
374 
375 			// Program.Config.MainWindow.Maximized = (ws == FormWindowState.Maximized);
376 		}
377 
378 		/// <summary>
379 		/// Update the UI state, i.e. enable/disable menu items depending on the state
380 		/// of the database (open, closed, locked, modified) and the selected items in
381 		/// the groups and entries list. You must call this function after all
382 		/// state-changing operations. For example, if you add a new entry the state
383 		/// needs to be updated (as the database has been modified) and you must call
384 		/// this function.
385 		/// </summary>
386 		/// <param name="bSetModified">If this parameter is <c>true</c>, the
387 		/// currently open database is marked as modified.</param>
UpdateUIState(bool bSetModified)388 		private void UpdateUIState(bool bSetModified)
389 		{
390 			UpdateUIState(bSetModified, null);
391 		}
392 
UpdateUIState(bool bSetModified, Control cOptFocus)393 		private void UpdateUIState(bool bSetModified, Control cOptFocus)
394 		{
395 			// For instance when running "START /MIN KeePass.exe",
396 			// we can get called before OnFormLoad has been called
397 			if(!m_bFormLoadCalled) return;
398 
399 			NotifyUserActivity();
400 			m_bUpdateUIStateOnce = false; // We do it now
401 
402 			MainAppState s = GetMainAppState();
403 			PwDatabase pd = m_docMgr.ActiveDatabase;
404 			PwEntry pe = s.SelectedEntry;
405 			ulong uif = Program.Config.UI.UIFlags;
406 
407 			if(s.DatabaseOpened && bSetModified) pd.Modified = true;
408 
409 			bool bGroupsEnabled = m_tvGroups.Enabled;
410 			if(bGroupsEnabled && !s.DatabaseOpened)
411 			{
412 				m_tvGroups.BackColor = AppDefs.ColorControlDisabled;
413 				m_tvGroups.Enabled = false;
414 			}
415 			else if(!bGroupsEnabled && s.DatabaseOpened)
416 			{
417 				m_tvGroups.Enabled = true;
418 				m_tvGroups.BackColor = AppDefs.ColorControlNormal;
419 			}
420 
421 			if(Program.Config.MainWindow.EntrySelGroupSel && (pe != null))
422 			{
423 				PwGroup pgInitial = GetSelectedGroup();
424 				PwGroup pgToSel = pe.ParentGroup;
425 				if((pgToSel != null) && (pgToSel != pgInitial))
426 					SetSelectedGroup(pgToSel, true);
427 			}
428 
429 			m_lvEntries.Enabled = s.DatabaseOpened;
430 
431 			m_statusPartSelected.Text = s.EntriesSelected.ToString() +
432 				" " + KPRes.OfLower + " " + s.EntriesCount.ToString() +
433 				" " + KPRes.SelectedLower;
434 			SetStatusEx(null);
435 
436 			string strWindowText = PwDefs.ShortProductName;
437 			string strNtfText = PwDefs.ShortProductName;
438 			// int qSmall = UIUtil.GetSmallIconSize().Width;
439 
440 			const int cchNtf = NativeMethods.NOTIFYICONDATA_TIP_SIZE - 1;
441 
442 			if(s.FileLocked)
443 			{
444 				IOConnectionInfo iocLck = m_docMgr.ActiveDocument.LockedIoc;
445 
446 				string strWindowEnd = @" [" + KPRes.Locked + @"] - " +
447 					PwDefs.ShortProductName;
448 				string strFileDesc = iocLck.Path;
449 				if(!Program.Config.MainWindow.ShowFullPathInTitle)
450 					strFileDesc = UrlUtil.GetFileName(strFileDesc);
451 				int iMaxChars = cchNtf - strWindowEnd.Length;
452 				if(iMaxChars >= 0)
453 					strFileDesc = WinUtil.CompactPath(strFileDesc, iMaxChars);
454 				else { Debug.Assert(false); }
455 				strWindowText = strFileDesc + strWindowEnd;
456 
457 				string strNtfPre = PwDefs.ShortProductName + " - " +
458 					KPRes.WorkspaceLocked + MessageService.NewLine;
459 				strFileDesc = iocLck.Path;
460 				iMaxChars = cchNtf - strNtfPre.Length;
461 				if(iMaxChars >= 0)
462 					strFileDesc = WinUtil.CompactPath(strFileDesc, iMaxChars);
463 				else { Debug.Assert(false); }
464 				strNtfText = strNtfPre + strFileDesc;
465 
466 				// Icon icoDisposable, icoAssignable;
467 				// CreateColorizedIcon(Properties.Resources.QuadLocked, qSmall,
468 				//	ref m_kvpIcoTrayLocked, out icoAssignable, out icoDisposable);
469 				// m_ntfTray.Icon = icoAssignable;
470 				// if(icoDisposable != null) icoDisposable.Dispose();
471 				m_ntfTray.Icon = CreateColorizedIcon(AppIconType.QuadLocked, true);
472 
473 				TaskbarList.SetOverlayIcon(this, Properties.Resources.LockOverlay,
474 					KPRes.Locked);
475 			}
476 			else if(!s.DatabaseOpened)
477 			{
478 				// Icon icoDisposable, icoAssignable;
479 				// CreateColorizedIcon(Properties.Resources.QuadNormal, qSmall,
480 				//	ref m_kvpIcoTrayNormal, out icoAssignable, out icoDisposable);
481 				// m_ntfTray.Icon = icoAssignable;
482 				// if(icoDisposable != null) icoDisposable.Dispose();
483 				m_ntfTray.Icon = CreateColorizedIcon(AppIconType.QuadNormal, true);
484 
485 				TaskbarList.SetOverlayIcon(this, null, string.Empty);
486 			}
487 			else // Database open and not locked
488 			{
489 				string strWindowEnd = (pd.Modified ? "* - " : " - ") +
490 					PwDefs.ShortProductName;
491 				string strFileDesc = pd.IOConnectionInfo.Path;
492 				if(!Program.Config.MainWindow.ShowFullPathInTitle)
493 					strFileDesc = UrlUtil.GetFileName(strFileDesc);
494 				strFileDesc = WinUtil.CompactPath(strFileDesc, cchNtf -
495 					strWindowEnd.Length);
496 				strWindowText = strFileDesc + strWindowEnd;
497 
498 				string strNtfPre = PwDefs.ShortProductName + MessageService.NewLine;
499 				strNtfText = strNtfPre + WinUtil.CompactPath(
500 					pd.IOConnectionInfo.Path, cchNtf - strNtfPre.Length);
501 
502 				// Icon icoDisposable, icoAssignable;
503 				// CreateColorizedIcon(Properties.Resources.QuadNormal, qSmall,
504 				//	ref m_kvpIcoTrayNormal, out icoAssignable, out icoDisposable);
505 				// m_ntfTray.Icon = icoAssignable;
506 				// if(icoDisposable != null) icoDisposable.Dispose();
507 				m_ntfTray.Icon = CreateColorizedIcon(AppIconType.QuadNormal, true);
508 
509 				TaskbarList.SetOverlayIcon(this, null, string.Empty);
510 			}
511 
512 			DwmUtil.EnableWindowPeekPreview(this);
513 
514 			bool bFormShownRaised = false;
515 			if(MonoWorkarounds.IsRequired(801414))
516 				bFormShownRaised = MonoWorkarounds.ExchangeFormShownRaised(this, false);
517 
518 			// Clip the strings again (it could be that a translator used
519 			// a string in KPRes that is too long to be displayed)
520 			this.Text = StrUtil.CompactString3Dots(strWindowText, cchNtf);
521 
522 			if(MonoWorkarounds.IsRequired(801414))
523 				MonoWorkarounds.ExchangeFormShownRaised(this, bFormShownRaised);
524 
525 			strNtfText = StrUtil.EncodeToolTipText(strNtfText);
526 			m_ntfTray.Text = StrUtil.CompactString3Dots(strNtfText, cchNtf);
527 
528 			// Icon icoToDispose, icoToAssign;
529 			// if(CreateColorizedIcon(Properties.Resources.KeePass, 0,
530 			//	ref m_kvpIcoMain, out icoToAssign, out icoToDispose))
531 			//	this.Icon = icoToAssign;
532 			// if(icoToDispose != null) icoToDispose.Dispose();
533 			this.Icon = CreateColorizedIcon(AppIconType.Main, false);
534 
535 			UIUtil.SetEnabledFast(s.DatabaseOpened || s.FileLocked,
536 				m_menuFileClose, m_tbCloseTab);
537 			UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuFileSaveAs,
538 				m_menuFileSaveAsLocal, m_menuFileSaveAsUrl, m_menuFileSaveAsCopy,
539 				m_menuFileChangeMasterKey, m_menuFilePrint,
540 				m_menuFilePrintDatabase, m_menuFilePrintEmSheet,
541 				m_menuFileImport, m_menuFileExport);
542 			UIUtil.SetEnabledFast(s.DatabaseOpened && pd.MasterKey.ContainsType(
543 				typeof(KcpKeyFile)), m_menuFilePrintKeyFile);
544 			UIUtil.SetEnabledFast(s.DatabaseOpened && ((uif &
545 				(ulong)AceUIFlags.DisableDbSettings) == 0), m_menuFileDbSettings);
546 			UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuFileSync,
547 				m_menuFileSyncFile, m_menuFileSyncUrl, m_menuFileSyncRecent);
548 			UIUtil.SetEnabledFast(s.EnableLockCmd, m_menuFileLock, m_tbLockWorkspace);
549 
550 			// Enable/disable menu items for correct shortcut keys handling
551 			UpdateUIGroupMenuState(s, false);
552 			UpdateUIEntryMenuState(s, false);
553 
554 			UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuFindInDatabase,
555 				m_menuFindInGroup, m_menuFindProfiles, m_menuFindTag, m_menuFindAll);
556 			UIUtil.SetEnabledFast((s.EntriesSelected == 1), m_menuFindParentGroup);
557 			UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuFindExp, m_menuFindExpIn,
558 				m_menuFindExp1, m_menuFindExp2, m_menuFindExp3, m_menuFindExp7,
559 				m_menuFindExp14, m_menuFindExp30, m_menuFindExp60, m_menuFindExpInF);
560 			UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuFindLastMod,
561 				m_menuFindLarge, m_menuFindDupPasswords,
562 				m_menuFindSimPasswordsP, m_menuFindSimPasswordsC,
563 				m_menuFindPwQuality);
564 
565 			UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuToolsGeneratePwList,
566 				m_menuToolsTanWizard, m_menuToolsDbMaintenance,
567 				m_menuToolsDbDelDupEntries, m_menuToolsDbDelEmptyGroups,
568 				m_menuToolsDbDelUnusedIcons);
569 			UIUtil.SetEnabledFast(s.DatabaseOpened && ((uif &
570 				(ulong)AceUIFlags.DisableXmlReplace) == 0), m_menuToolsDbXmlRep);
571 
572 			UIUtil.SetEnabledFast(s.DatabaseOpened, m_tbAddEntry, m_tbFind,
573 				m_tbEntryViewsDropDown, m_tbViewsShowAll, m_tbViewsShowExpired,
574 				m_tbQuickFind);
575 
576 			Image imgSave;
577 			bool bEnableSave;
578 			if(Program.Config.MainWindow.DisableSaveIfNotModified)
579 			{
580 				imgSave = m_imgFileSaveEnabled;
581 				bEnableSave = (s.DatabaseOpened && pd.Modified);
582 			}
583 			else
584 			{
585 				imgSave = ((!s.DatabaseOpened || pd.Modified) ?
586 					m_imgFileSaveEnabled : m_imgFileSaveDisabled);
587 				bEnableSave = s.DatabaseOpened;
588 			}
589 			m_menuFileSave.Image = imgSave;
590 			m_tbSaveDatabase.Image = imgSave;
591 			UIUtil.SetEnabledFast(bEnableSave, m_menuFileSave, m_tbSaveDatabase);
592 
593 			m_tbCopyUserName.Enabled = s.CanCopyUserName;
594 			m_tbCopyPassword.Enabled = s.CanCopyPassword;
595 			UIUtil.SetEnabledFast(s.CanOpenUrl, m_tbOpenUrl,
596 				m_tbOpenUrlDefault, m_tbCopyUrl);
597 			m_tbAutoType.Enabled = s.CanPerformAutoType;
598 
599 			m_menuFileLock.Text = s.LockUnlock;
600 			string strLockText = StrUtil.RemoveAccelerator(s.LockUnlock);
601 			m_tbLockWorkspace.Text = m_ctxTrayLock.Text = strLockText;
602 			m_tbLockWorkspace.ToolTipText = strLockText + " (" +
603 				m_menuFileLock.ShortcutKeyDisplayString + ")";
604 
605 			bool bMultiDoc = (m_docMgr.DocumentCount > 1);
606 			m_tabMain.Visible = bMultiDoc;
607 			m_tbSaveAll.Visible = bMultiDoc;
608 			m_tbCloseTab.Visible = (bMultiDoc ||
609 				!Program.Config.MainWindow.HideCloseDatabaseButton);
610 
611 			bool bAtLeastOneModified = false;
612 			foreach(PwDocument pwDoc in m_docMgr.Documents)
613 			{
614 				if((pwDoc == null) || (pwDoc.Database == null)) { Debug.Assert(false); continue; }
615 				bAtLeastOneModified |= pwDoc.Database.Modified;
616 			}
617 			// m_tbSaveAll.Image = (bAtLeastOneModified ? m_imgFileSaveAllEnabled :
618 			//	m_imgFileSaveAllDisabled);
619 			m_tbSaveAll.Enabled = bAtLeastOneModified;
620 
621 			ShowEntryDetails(pe);
622 			UpdateUITabs();
623 
624 			if(cOptFocus != null) ResetDefaultFocus(cOptFocus);
625 
626 			if(this.UIStateUpdated != null) this.UIStateUpdated(this, EventArgs.Empty);
627 			// Program.TriggerSystem.RaiseEvent(EcasEventIDs.UpdatedUIState);
628 		}
629 
UpdateUIGroupState(MainAppState sMain, bool bMenuVisible, bool bContextMenu)630 		private void UpdateUIGroupState(MainAppState sMain, bool bMenuVisible,
631 			bool bContextMenu)
632 		{
633 			MainAppState s = (sMain ?? GetMainAppState());
634 
635 			PwGroup pg = GetSelectedGroup();
636 			PwGroup pgParent = ((pg != null) ? pg.ParentGroup : null);
637 			PwDatabase pd = m_docMgr.ActiveDatabase;
638 			if(!s.DatabaseOpened) pd = null; // Null if closed
639 			PwGroup pgRoot = ((pd != null) ? pd.RootGroup : null);
640 
641 			bool bChildOps = (s.DatabaseOpened && (pg != pgRoot));
642 			bool bMoveOps = (bChildOps && (pgParent != null) &&
643 				(pgParent.Groups.UCount > 1));
644 			uint uSubGroups = 0, uSubEntries = 0;
645 			if(pg != null) pg.GetCounts(true, out uSubGroups, out uSubEntries);
646 
647 			m_menuMain.SuspendLayout();
648 			m_ctxGroupList.SuspendLayout();
649 
650 			UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuGroupAdd, m_menuGroupEdit);
651 			UIUtil.SetEnabledFast(bChildOps, m_menuGroupDuplicate, m_menuGroupDelete);
652 
653 			UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuGroupRearrange);
654 			UIUtil.SetEnabledFast((bMoveOps && (pgParent.Groups.IndexOf(pg) >= 1)),
655 				m_menuGroupMoveToTop, m_menuGroupMoveOneUp);
656 			UIUtil.SetEnabledFast((bMoveOps && (pgParent.Groups.IndexOf(pg) <
657 				((int)pgParent.Groups.UCount - 1))), m_menuGroupMoveOneDown,
658 				m_menuGroupMoveToBottom);
659 
660 			if(bMenuVisible)
661 				UpdateMoveToPreviousParentGroupUI(pg, null, m_menuGroupMoveToPreviousParent);
662 
663 			UIUtil.SetEnabledFast(((pg != null) && (pg.Groups.UCount > 1)), m_menuGroupSort);
664 			UIUtil.SetEnabledFast((uSubGroups > 1), m_menuGroupSortRec);
665 
666 			UIUtil.SetEnabledFast((uSubGroups > 0), m_menuGroupExpand, m_menuGroupCollapse);
667 
668 			bool bShowEmpty = false, bEnableEmpty = false;
669 			Debug.Assert(m_menuGroupEmptyRB.ShortcutKeys == Keys.None); // For bMenuVisible
670 			if(bMenuVisible && s.DatabaseOpened && pd.RecycleBinEnabled)
671 			{
672 				PwGroup pgRB = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);
673 
674 				bShowEmpty = (pgRB != null);
675 				if(bContextMenu) bShowEmpty &= (pg == pgRB);
676 
677 				if(bShowEmpty)
678 					bEnableEmpty = ((pgRB.Groups.UCount > 0) || (pgRB.Entries.UCount > 0));
679 			}
680 			m_menuGroupEmptyRB.Enabled = bEnableEmpty;
681 			m_milMain.SetCopyAvailable(m_menuGroupEmptyRB, bShowEmpty);
682 
683 			if(bContextMenu)
684 				UIUtil.SetEnabledFast(s.DatabaseOpened, m_ctxGroupFind, m_ctxGroupFindProfiles);
685 
686 			// 'Paste Group' is updated in the menu opening handler
687 			UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuGroupDX, m_menuGroupClipCopy,
688 				m_menuGroupClipCopyPlain, m_menuGroupPrint, m_menuGroupExport);
689 
690 			m_ctxGroupList.ResumeLayout(true);
691 			m_menuMain.ResumeLayout(true);
692 		}
693 
UpdateUIGroupMenuState(MainAppState sMain, bool bMenuVisible)694 		private void UpdateUIGroupMenuState(MainAppState sMain, bool bMenuVisible)
695 		{
696 			UpdateUIGroupState(sMain, bMenuVisible, false);
697 		}
698 
UpdateUIGroupCtxState()699 		private void UpdateUIGroupCtxState()
700 		{
701 			UpdateUIGroupState(null, true, true);
702 		}
703 
UpdateUIEntryState(MainAppState sMain, bool bMenuVisible, bool bContextMenu, DynamicMenu dynStrings, DynamicMenu dynBinaries)704 		private void UpdateUIEntryState(MainAppState sMain, bool bMenuVisible,
705 			bool bContextMenu, DynamicMenu dynStrings, DynamicMenu dynBinaries)
706 		{
707 			MainAppState s = (sMain ?? GetMainAppState());
708 			PwEntry pe = s.SelectedEntry;
709 
710 			m_menuMain.SuspendLayout();
711 			m_ctxPwList.SuspendLayout();
712 
713 			m_menuEntryCopyUserName.Enabled = s.CanCopyUserName;
714 			m_milMain.SetCopyAvailable(m_menuEntryCopyUserName, !s.IsOneTan);
715 
716 			m_menuEntryCopyPassword.Enabled = s.CanCopyPassword;
717 			m_menuEntryCopyPassword.Text = (s.IsOneTan ? KPRes.CopyTanMenu :
718 				KPRes.CopyPasswordMenu);
719 
720 			UIUtil.SetEnabledFast(s.CanOpenUrl, m_menuEntryUrl, m_menuEntryOpenUrl,
721 				m_menuEntryCopyUrl);
722 
723 			if(bMenuVisible)
724 			{
725 				dynStrings.Clear();
726 				dynBinaries.Clear();
727 
728 				uint uStrItems = 0, uBinItems = 0;
729 				if((s.EntriesSelected == 1) && (pe != null))
730 				{
731 					List<char> lAvailKeys = new List<char>(PwCharSet.MenuAccels);
732 					foreach(KeyValuePair<string, ProtectedString> kvp in pe.Strings)
733 					{
734 						string strText = kvp.Key;
735 						if(PwDefs.IsStandardField(strText)) continue;
736 
737 						strText = StrUtil.EncodeMenuText(strText);
738 						strText = StrUtil.AddAccelerator(strText, lAvailKeys);
739 
740 						dynStrings.AddItem(strText, Properties.Resources.B16x16_KGPG_Info,
741 							kvp.Key);
742 						++uStrItems;
743 					}
744 
745 					lAvailKeys = new List<char>(PwCharSet.MenuAccels);
746 					foreach(KeyValuePair<string, ProtectedBinary> kvp in pe.Binaries)
747 					{
748 						string strText = StrUtil.EncodeMenuText(kvp.Key);
749 						strText = StrUtil.AddAccelerator(strText, lAvailKeys);
750 
751 						Image imgIcon = FileIcons.GetImageForName(kvp.Key, null);
752 
753 						EntryBinaryDataContext ctxBin = new EntryBinaryDataContext();
754 						ctxBin.Entry = pe;
755 						ctxBin.Name = kvp.Key;
756 
757 						dynBinaries.AddItem(strText, imgIcon, ctxBin);
758 						++uBinItems;
759 					}
760 				}
761 
762 				if(uStrItems == 0) dynStrings.AddItem(m_strNoneP, null).Enabled = false;
763 				if(uBinItems == 0) dynBinaries.AddItem(m_strNoneP, null).Enabled = false;
764 
765 				m_menuEntryCopyString.Enabled = (uStrItems != 0);
766 				m_milMain.SetCopyAvailable(m_menuEntryCopyString, (uStrItems != 0));
767 				m_menuEntryAttachments.Enabled = (uBinItems != 0);
768 				m_milMain.SetCopyAvailable(m_menuEntryAttachments, (uBinItems != 0));
769 			}
770 
771 			bool bAttach = ((s.EntriesSelected >= 2) || ((pe != null) &&
772 				(pe.Binaries.UCount > 0)));
773 			m_menuEntrySaveAttachedFiles.Enabled = bAttach;
774 			m_milMain.SetCopyAvailable(m_menuEntrySaveAttachedFiles, bAttach);
775 
776 			m_menuEntryPerformAutoType.Enabled = s.CanPerformAutoType;
777 
778 			m_menuEntryAutoTypeAdv.Enabled = s.CanPerformAutoType;
779 			m_menuEntryAutoTypeAdv.Available = Program.Config.UI.ShowAdvAutoTypeCommands;
780 			foreach(ToolStripItem tsiAdv in m_menuEntryAutoTypeAdv.DropDownItems)
781 			{
782 				tsiAdv.Enabled = s.CanPerformAutoType;
783 			}
784 			foreach(ToolStripItem tsiAdv in m_ctxEntryAutoTypeAdv.DropDownItems)
785 			{
786 				tsiAdv.Enabled = s.CanPerformAutoType;
787 			}
788 
789 			UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuEntryAdd);
790 			UIUtil.SetEnabledFast((s.EntriesSelected != 0), m_menuEntryEdit,
791 				m_menuEntryEditQuick, m_menuEntryIcon,
792 				m_menuEntryColor, m_menuEntryColorStandard,
793 				m_menuEntryColorLightRed, m_menuEntryColorLightGreen,
794 				m_menuEntryColorLightBlue, m_menuEntryColorLightYellow,
795 				m_menuEntryColorCustom, m_menuEntryTagAdd, m_menuEntryTagRemove,
796 				m_menuEntryTagNew, m_menuEntryExpiresNow, m_menuEntryExpiresNever,
797 				m_menuEntryDuplicate, m_menuEntryDelete);
798 
799 			UIUtil.SetEnabledFast((s.DatabaseOpened && (s.EntriesCount > 0)),
800 				m_menuEntrySelectAll);
801 
802 			UIUtil.SetEnabledFast((s.EntriesSelected != 0), m_menuEntryRearrange,
803 				m_menuEntryMoveToGroup);
804 			UIUtil.SetEnabledFast(((m_pListSorter.Column < 0) && (s.EntriesSelected > 0)),
805 				m_menuEntryMoveToTop, m_menuEntryMoveOneUp, m_menuEntryMoveOneDown,
806 				m_menuEntryMoveToBottom);
807 
808 			if(bMenuVisible)
809 			{
810 				PwEntry[] v = GetSelectedEntries();
811 				UpdateMoveToPreviousParentGroupUI(null, v, m_menuEntryMoveToPreviousParent);
812 			}
813 
814 			UIUtil.SetEnabledFast(s.DatabaseOpened, m_menuEntryDX);
815 			// 'Paste Entry' is updated in the menu opening handler
816 			// and its keyboard shortcut is handled manually (i.e.
817 			// it's independent of the menu item state)
818 			UIUtil.SetEnabledFast((s.EntriesSelected != 0), m_menuEntryClipCopy,
819 				m_menuEntryClipCopyPlain, m_menuEntryPrint, m_menuEntryExport);
820 
821 			m_ctxPwList.ResumeLayout(true);
822 			m_menuMain.ResumeLayout(true);
823 		}
824 
UpdateUIEntryMenuState(MainAppState sMain, bool bMenuVisible)825 		private void UpdateUIEntryMenuState(MainAppState sMain, bool bMenuVisible)
826 		{
827 			UpdateUIEntryState(sMain, bMenuVisible, false, m_dynStringsMenu,
828 				m_dynBinariesMenu);
829 		}
830 
UpdateUIEntryCtxState()831 		private void UpdateUIEntryCtxState()
832 		{
833 			UpdateUIEntryState(null, true, true, m_dynStringsCtx,
834 				m_dynBinariesCtx);
835 		}
836 
GetMainAppState()837 		private MainAppState GetMainAppState()
838 		{
839 			PwDatabase pd = m_docMgr.ActiveDatabase;
840 			PwEntry pe = GetSelectedEntry(true);
841 
842 			MainAppState s = new MainAppState();
843 
844 			s.FileLocked = IsFileLocked(null);
845 			s.DatabaseOpened = ((pd != null) && pd.IsOpen);
846 			s.EntriesCount = (s.DatabaseOpened ? m_lvEntries.Items.Count : 0);
847 			s.EntriesSelected = (s.DatabaseOpened ? m_lvEntries.SelectedIndices.Count : 0);
848 			s.EnableLockCmd = (s.DatabaseOpened || s.FileLocked);
849 			s.NoWindowShown = (GlobalWindowManager.WindowCount == 0);
850 			s.SelectedEntry = pe;
851 
852 			if(pe != null)
853 			{
854 				if(s.EntriesSelected == 1)
855 				{
856 					s.CanCopyUserName = !pe.Strings.GetSafe(PwDefs.UserNameField).IsEmpty;
857 					s.CanCopyPassword = !pe.Strings.GetSafe(PwDefs.PasswordField).IsEmpty;
858 					s.CanPerformAutoType = pe.GetAutoTypeEnabled();
859 					s.IsOneTan = PwDefs.IsTanEntry(pe);
860 				}
861 
862 				s.CanOpenUrl = ((s.EntriesSelected > 1) ||
863 					!pe.Strings.GetSafe(PwDefs.UrlField).IsEmpty);
864 			}
865 
866 			s.LockUnlock = (s.FileLocked ? KPRes.LockMenuUnlock : KPRes.LockMenuLock);
867 
868 			return s;
869 		}
870 
871 		/// <summary>
872 		/// Set the main status bar text.
873 		/// </summary>
874 		/// <param name="strStatusText">New status bar text.</param>
SetStatusEx(string strStatusText)875 		public void SetStatusEx(string strStatusText)
876 		{
877 			if(strStatusText == null) m_statusPartInfo.Text = m_strStatusReady;
878 			else m_statusPartInfo.Text = strStatusText;
879 		}
880 
UpdateClipboardStatus()881 		private void UpdateClipboardStatus()
882 		{
883 			// Fix values in case the maximum time has been changed in the
884 			// options while the countdown is running
885 			if((m_nClipClearMax < 0) && (m_nClipClearCur > 0))
886 				m_nClipClearCur = 0;
887 			else if((m_nClipClearCur > m_nClipClearMax) && (m_nClipClearMax >= 0))
888 				m_nClipClearCur = m_nClipClearMax;
889 
890 			if((m_nClipClearCur > 0) && (m_nClipClearMax > 0))
891 				m_statusClipboard.Value = ((m_nClipClearCur * 100) / m_nClipClearMax);
892 			else if(m_nClipClearCur == 0)
893 				m_statusClipboard.Visible = false;
894 		}
895 
896 		/// <summary>
897 		/// Start the clipboard countdown (set the current tick count to the
898 		/// maximum value and decrease it each second -- at 0 the clipboard
899 		/// is cleared automatically). This function is asynchronous.
900 		/// </summary>
StartClipboardCountdown()901 		public void StartClipboardCountdown()
902 		{
903 			if(m_nClipClearMax >= 0)
904 			{
905 				m_nClipClearCur = m_nClipClearMax;
906 
907 				m_statusClipboard.Visible = true;
908 				UpdateClipboardStatus();
909 
910 				string strText = KPRes.ClipboardDataCopied + " " +
911 					KPRes.ClipboardClearInSeconds + ".";
912 				strText = strText.Replace(@"[PARAM]", m_nClipClearMax.ToString());
913 
914 				SetStatusEx(strText);
915 
916 				// if(m_ntfTray.Visible)
917 				//	m_ntfTray.ShowBalloonTip(0, KPRes.ClipboardAutoClear,
918 				//		strText, ToolTipIcon.Info);
919 			}
920 		}
921 
922 		/// <summary>
923 		/// Gets the focused or first selected entry.
924 		/// </summary>
925 		/// <returns>Matching entry or <c>null</c>.</returns>
GetSelectedEntry(bool bRequireSelected)926 		public PwEntry GetSelectedEntry(bool bRequireSelected)
927 		{
928 			return GetSelectedEntry(bRequireSelected, false);
929 		}
930 
GetSelectedEntry(bool bRequireSelected, bool bGetLastSelectedEntry)931 		public PwEntry GetSelectedEntry(bool bRequireSelected, bool bGetLastSelectedEntry)
932 		{
933 			if(!m_docMgr.ActiveDatabase.IsOpen) return null;
934 
935 			if(!bRequireSelected)
936 			{
937 				ListViewItem lviFocused = m_lvEntries.FocusedItem;
938 				if(lviFocused != null) return ((PwListItem)lviFocused.Tag).Entry;
939 			}
940 
941 			ListView.SelectedListViewItemCollection coll = m_lvEntries.SelectedItems;
942 			if(coll.Count > 0)
943 			{
944 				ListViewItem lvi = coll[bGetLastSelectedEntry ? (coll.Count - 1) : 0];
945 				if(lvi != null) return ((PwListItem)lvi.Tag).Entry;
946 			}
947 
948 			return null;
949 		}
950 
951 		/// <summary>
952 		/// Get all selected entries.
953 		/// </summary>
954 		/// <returns>A list of all selected entries.</returns>
GetSelectedEntries()955 		public PwEntry[] GetSelectedEntries()
956 		{
957 			if(!m_docMgr.ActiveDatabase.IsOpen) return null;
958 
959 			ListView.SelectedListViewItemCollection coll = m_lvEntries.SelectedItems;
960 			if(coll == null) { Debug.Assert(false); return null; }
961 			int n = coll.Count; // Getting Count sends a message
962 			if(n == 0) return null;
963 
964 			PwEntry[] vSelected = new PwEntry[n];
965 			int i = 0;
966 			// LVSLVIC: one access by index requires O(n) time, thus use
967 			// enumerator instead (which requires O(1) for each element)
968 			foreach(ListViewItem lvi in coll)
969 			{
970 				if(i >= n) { Debug.Assert(false); break; }
971 
972 				vSelected[i] = ((PwListItem)lvi.Tag).Entry;
973 				++i;
974 			}
975 			Debug.Assert(i == n);
976 
977 			return vSelected;
978 		}
979 
GetSelectedEntriesCount()980 		public uint GetSelectedEntriesCount()
981 		{
982 			if(!m_docMgr.ActiveDatabase.IsOpen) return 0;
983 
984 			return (uint)m_lvEntries.SelectedIndices.Count;
985 		}
986 
GetSelectedEntriesAsGroup()987 		public PwGroup GetSelectedEntriesAsGroup()
988 		{
989 			PwGroup pg = new PwGroup(true, true);
990 			pg.IsVirtual = true;
991 
992 			// Copying group properties would confuse users
993 			// PwGroup pgSel = GetSelectedGroup();
994 			// if(pgSel != null)
995 			// {
996 			//	pg.Name = pgSel.Name;
997 			//	pg.IconId = pgSel.IconId;
998 			//	pg.CustomIconUuid = pgSel.CustomIconUuid;
999 			// }
1000 
1001 			PwEntry[] vSel = GetSelectedEntries();
1002 			if((vSel == null) || (vSel.Length == 0)) return pg;
1003 
1004 			foreach(PwEntry pe in vSel) pg.AddEntry(pe, false);
1005 
1006 			return pg;
1007 		}
1008 
1009 		/// <summary>
1010 		/// Get the currently selected group. The selected <c>TreeNode</c> is
1011 		/// automatically translated to a <c>PwGroup</c>.
1012 		/// </summary>
1013 		/// <returns>Selected <c>PwGroup</c>.</returns>
GetSelectedGroup()1014 		public PwGroup GetSelectedGroup()
1015 		{
1016 			if(!m_docMgr.ActiveDatabase.IsOpen) return null;
1017 
1018 			TreeNode tn = m_tvGroups.SelectedNode;
1019 			if(tn == null) return null;
1020 			return (tn.Tag as PwGroup);
1021 		}
1022 
SetSelectedGroup(PwGroup pg, bool bEnsureVisible)1023 		internal void SetSelectedGroup(PwGroup pg, bool bEnsureVisible)
1024 		{
1025 			if(pg == null) { Debug.Assert(false); return; }
1026 
1027 			TreeNode tn = GuiFindGroup(pg.Uuid, null);
1028 			if(tn != null)
1029 			{
1030 				m_tvGroups.SelectedNode = tn;
1031 
1032 				if(bEnsureVisible) tn.EnsureVisible();
1033 			}
1034 			else { Debug.Assert(false); }
1035 		}
1036 
1037 		/// <summary>
1038 		/// Create or set an entry list view item.
1039 		/// </summary>
1040 		/// <param name="pe">Entry.</param>
1041 		/// <param name="lviTarget">If <c>null</c>, a new list view item is
1042 		/// created and added to the list (a group is created if necessary).
1043 		/// If not <c>null</c>, the properties are stored in this item (no
1044 		/// list view group is created and the list view item is not added
1045 		/// to the list).</param>
1046 		/// <returns>Created or modified list view item.</returns>
SetListEntry(PwEntry pe, ListViewItem lviTarget)1047 		private ListViewItem SetListEntry(PwEntry pe, ListViewItem lviTarget)
1048 		{
1049 			if(pe == null) { Debug.Assert(false); return null; }
1050 
1051 			ListViewItem lvi = (lviTarget ?? new ListViewItem());
1052 
1053 			PwListItem pli = new PwListItem(pe);
1054 			if(lviTarget == null) lvi.Tag = pli; // Lock below (when adding it)
1055 			else
1056 			{
1057 				lock(m_asyncListUpdate.ListEditSyncObject) { lvi.Tag = pli; }
1058 			}
1059 
1060 			int iIndexHint = ((lviTarget != null) ? lviTarget.Index :
1061 				m_lvEntries.Items.Count);
1062 
1063 			if(pe.Expires && (pe.ExpiryTime <= m_dtCachedNow))
1064 			{
1065 				lvi.ImageIndex = (int)PwIcon.Expired;
1066 				if(m_fontExpired != null) lvi.Font = m_fontExpired;
1067 			}
1068 			else // Not expired
1069 			{
1070 				// Reset font, if item was expired previously (i.e. has expired font)
1071 				if((lviTarget != null) && (lvi.ImageIndex == (int)PwIcon.Expired))
1072 					lvi.Font = m_lvEntries.Font;
1073 
1074 				if(pe.CustomIconUuid.Equals(PwUuid.Zero))
1075 					lvi.ImageIndex = (int)pe.IconId;
1076 				else
1077 					lvi.ImageIndex = (int)PwIcon.Count +
1078 						m_docMgr.ActiveDatabase.GetCustomIconIndex(pe.CustomIconUuid);
1079 			}
1080 
1081 			if(m_bEntryGrouping && (lviTarget == null))
1082 			{
1083 				PwGroup pgContainer = pe.ParentGroup;
1084 				PwGroup pgLast = ((m_lvgLastEntryGroup != null) ?
1085 					(PwGroup)m_lvgLastEntryGroup.Tag : null);
1086 
1087 				Debug.Assert(pgContainer != null);
1088 				if(pgContainer != null)
1089 				{
1090 					if(pgContainer != pgLast)
1091 					{
1092 						m_lvgLastEntryGroup = new ListViewGroup(
1093 							pgContainer.GetFullPath(" - ", false));
1094 						m_lvgLastEntryGroup.Tag = pgContainer;
1095 
1096 						m_lvEntries.Groups.Add(m_lvgLastEntryGroup);
1097 					}
1098 
1099 					lvi.Group = m_lvgLastEntryGroup;
1100 				}
1101 			}
1102 
1103 			if(!UIUtil.ColorsEqual(pe.ForegroundColor, Color.Empty))
1104 				lvi.ForeColor = pe.ForegroundColor;
1105 			else if(lviTarget != null) lvi.ForeColor = m_lvEntries.ForeColor;
1106 			else { Debug.Assert(UIUtil.ColorsEqual(lvi.ForeColor, m_lvEntries.ForeColor)); }
1107 
1108 			if(!UIUtil.ColorsEqual(pe.BackgroundColor, Color.Empty))
1109 				lvi.BackColor = pe.BackgroundColor;
1110 			// else if(Program.Config.MainWindow.EntryListAlternatingBgColors &&
1111 			//	((m_lvEntries.Items.Count & 1) == 1))
1112 			//	lvi.BackColor = m_clrAlternateItemBgColor;
1113 			else if(lviTarget != null) lvi.BackColor = m_lvEntries.BackColor;
1114 			else { Debug.Assert(UIUtil.ColorsEqual(lvi.BackColor, m_lvEntries.BackColor)); }
1115 
1116 			bool bAsync;
1117 
1118 			// m_bOnlyTans &= PwDefs.IsTanEntry(pe);
1119 			if(m_bShowTanIndices && m_bOnlyTans)
1120 			{
1121 				string strIndex = pe.Strings.ReadSafe(PwDefs.TanIndexField);
1122 
1123 				// KPF 1151
1124 				if(Program.Config.MainWindow.EntryListShowDerefData &&
1125 					SprEngine.MightDeref(strIndex))
1126 					strIndex = AsyncPwListUpdate.SprCompileFn(strIndex, pli);
1127 
1128 				if(strIndex.Length != 0) lvi.Text = strIndex;
1129 				else lvi.Text = PwDefs.TanTitle;
1130 			}
1131 			else
1132 			{
1133 				string strMain = GetEntryFieldEx(pe, 0, true, out bAsync);
1134 				lvi.Text = strMain;
1135 				if(bAsync)
1136 					m_asyncListUpdate.Queue(strMain, pli, iIndexHint, 0,
1137 						AsyncPwListUpdate.SprCompileFn);
1138 			}
1139 
1140 			int nColumns = m_lvEntries.Columns.Count;
1141 			if(lviTarget == null)
1142 			{
1143 				for(int iColumn = 1; iColumn < nColumns; ++iColumn)
1144 				{
1145 					string strSub = GetEntryFieldEx(pe, iColumn, true, out bAsync);
1146 					lvi.SubItems.Add(strSub);
1147 					if(bAsync)
1148 						m_asyncListUpdate.Queue(strSub, pli, iIndexHint, iColumn,
1149 							AsyncPwListUpdate.SprCompileFn);
1150 				}
1151 			}
1152 			else
1153 			{
1154 				int nSubItems = lvi.SubItems.Count;
1155 				for(int iColumn = 1; iColumn < nColumns; ++iColumn)
1156 				{
1157 					string strSub = GetEntryFieldEx(pe, iColumn, true, out bAsync);
1158 
1159 					if(iColumn < nSubItems) lvi.SubItems[iColumn].Text = strSub;
1160 					else lvi.SubItems.Add(strSub);
1161 
1162 					if(bAsync)
1163 						m_asyncListUpdate.Queue(strSub, pli, iIndexHint, iColumn,
1164 							AsyncPwListUpdate.SprCompileFn);
1165 				}
1166 
1167 				Debug.Assert(lvi.SubItems.Count == nColumns);
1168 			}
1169 
1170 			if(lviTarget == null)
1171 			{
1172 				lock(m_asyncListUpdate.ListEditSyncObject)
1173 				{
1174 					m_lvEntries.Items.Add(lvi);
1175 				}
1176 			}
1177 			return lvi;
1178 		}
1179 
AddEntriesToList(PwObjectList<PwEntry> vEntries)1180 		private void AddEntriesToList(PwObjectList<PwEntry> vEntries)
1181 		{
1182 			if(vEntries == null) { Debug.Assert(false); return; }
1183 
1184 			m_bEntryGrouping = m_lvEntries.ShowGroups;
1185 
1186 			ListViewStateEx lvseCachedState = new ListViewStateEx(m_lvEntries);
1187 			foreach(PwEntry pe in vEntries)
1188 			{
1189 				if(pe == null) { Debug.Assert(false); continue; }
1190 
1191 				if(m_bEntryGrouping)
1192 				{
1193 					PwGroup pg = pe.ParentGroup;
1194 
1195 					foreach(ListViewGroup lvg in m_lvEntries.Groups)
1196 					{
1197 						PwGroup pgList = (lvg.Tag as PwGroup);
1198 						Debug.Assert(pgList != null);
1199 						if((pgList != null) && (pg == pgList))
1200 						{
1201 							m_lvgLastEntryGroup = lvg;
1202 							break;
1203 						}
1204 					}
1205 				}
1206 
1207 				SetListEntry(pe, null);
1208 			}
1209 
1210 			Debug.Assert(lvseCachedState.CompareTo(m_lvEntries));
1211 
1212 			UIUtil.SetAlternatingBgColors(m_lvEntries, m_clrAlternateItemBgColor,
1213 				Program.Config.MainWindow.EntryListAlternatingBgColors);
1214 		}
1215 
UpdateGroupList(PwGroup pgToSelect)1216 		private void UpdateGroupList(PwGroup pgToSelect)
1217 		{
1218 			NotifyUserActivity();
1219 
1220 			PwDatabase pd = m_docMgr.ActiveDatabase;
1221 			PwGroup pgSel = (pgToSelect ?? GetSelectedGroup());
1222 
1223 			PwGroup pgTop = null;
1224 			TreeNode tnTop = m_tvGroups.TopNode;
1225 			if(tnTop != null) pgTop = (tnTop.Tag as PwGroup);
1226 			tnTop = null;
1227 
1228 			m_tvGroups.BeginUpdate();
1229 			m_tvGroups.Nodes.Clear();
1230 
1231 			UpdateImageLists(false);
1232 
1233 			m_dtCachedNow = DateTime.UtcNow;
1234 
1235 			TreeNode tnSel = null;
1236 			TreeNode tnRoot = (pd.IsOpen ? GuiAddGroupRec(m_tvGroups.Nodes,
1237 				pd.RootGroup, pgSel, ref tnSel, pgTop, ref tnTop) : null);
1238 
1239 			if(tnRoot != null) tnRoot.Expand();
1240 
1241 			if(tnSel != null)
1242 			{
1243 				// Ensure all parent tree nodes are expanded
1244 				List<TreeNode> lParents = new List<TreeNode>();
1245 				TreeNode tnUp = tnSel.Parent;
1246 				while(tnUp != null)
1247 				{
1248 					lParents.Add(tnUp);
1249 					tnUp = tnUp.Parent;
1250 				}
1251 				for(int i = (lParents.Count - 1); i >= 0; --i)
1252 					lParents[i].Expand();
1253 
1254 				m_tvGroups.SelectedNode = tnSel;
1255 			}
1256 			else if(tnRoot != null) m_tvGroups.SelectedNode = tnRoot;
1257 
1258 			// Restore view *after* changing the selection
1259 			if(tnTop != null) m_tvGroups.TopNode = tnTop;
1260 
1261 			m_tvGroups.EndUpdate();
1262 		}
1263 
GuiAddGroupRec(TreeNodeCollection tncContainer, PwGroup pg, PwGroup pgFind1, ref TreeNode tnFound1, PwGroup pgFind2, ref TreeNode tnFound2)1264 		private TreeNode GuiAddGroupRec(TreeNodeCollection tncContainer,
1265 			PwGroup pg, PwGroup pgFind1, ref TreeNode tnFound1,
1266 			PwGroup pgFind2, ref TreeNode tnFound2)
1267 		{
1268 			if(tncContainer == null) { Debug.Assert(false); return null; }
1269 			if(pg == null) { Debug.Assert(false); return null; }
1270 
1271 			PwDatabase pd = m_docMgr.ActiveDatabase;
1272 			bool bExpired = (pg.Expires && (pg.ExpiryTime <= m_dtCachedNow));
1273 			string strName = pg.Name; // + GetGroupSuffixText(pg);
1274 
1275 			int nIconID = ((!pg.CustomIconUuid.Equals(PwUuid.Zero)) ?
1276 				((int)PwIcon.Count + pd.GetCustomIconIndex(pg.CustomIconUuid)) :
1277 				(int)pg.IconId);
1278 			if(bExpired) nIconID = (int)PwIcon.Expired;
1279 
1280 			TreeNode tn = new TreeNode(strName, nIconID, nIconID);
1281 			tn.Tag = pg;
1282 			UIUtil.SetGroupNodeToolTip(tn, pg);
1283 
1284 			if((pg == pd.RootGroup) && (m_fontBoldTree != null))
1285 				tn.NodeFont = m_fontBoldTree;
1286 			else if(pd.RecycleBinEnabled && pg.Uuid.Equals(pd.RecycleBinUuid) &&
1287 				(m_fontItalicTree != null))
1288 				tn.NodeFont = m_fontItalicTree;
1289 			else if(bExpired && (m_fontExpired != null))
1290 				tn.NodeFont = m_fontExpired;
1291 
1292 			tncContainer.Add(tn);
1293 
1294 			foreach(PwGroup pgSub in pg.Groups)
1295 				GuiAddGroupRec(tn.Nodes, pgSub, pgFind1, ref tnFound1,
1296 					pgFind2, ref tnFound2);
1297 
1298 			if(tn.Nodes.Count != 0)
1299 			{
1300 				bool bExpanded = tn.IsExpanded;
1301 				if(bExpanded && !pg.IsExpanded) tn.Collapse();
1302 				else if(!bExpanded && pg.IsExpanded) tn.Expand();
1303 			}
1304 
1305 			if(pg == pgFind1) tnFound1 = tn;
1306 			if(pg == pgFind2) tnFound2 = tn;
1307 			return tn;
1308 		}
1309 
1310 		/// <summary>
1311 		/// Update the entries list. This function completely rebuilds the entries
1312 		/// list. You must call this function after you've made any changes to
1313 		/// the entries of the currently selected group. Note that if you only
1314 		/// made small changes (like editing an existing entry), the
1315 		/// <c>RefreshEntriesList</c> function could be a better choice, as it only
1316 		/// updates currently listed items and doesn't rebuild the whole list as
1317 		/// <c>UpdateEntryList</c>.
1318 		/// </summary>
1319 		/// <param name="pgSelected">Group whose entries should be shown. If this
1320 		/// parameter is <c>null</c>, the entries of the currently selected group
1321 		/// (groups view) are displayed, otherwise the entries of the <c>pgSelected</c>
1322 		/// group are displayed.</param>
UpdateEntryList(PwGroup pgSelected, bool bOnlyUpdateCurrentlyShown)1323 		private void UpdateEntryList(PwGroup pgSelected, bool bOnlyUpdateCurrentlyShown)
1324 		{
1325 			NotifyUserActivity();
1326 
1327 			UpdateImageLists(false);
1328 
1329 			PwEntry peTop = GetTopEntry(), peFocused = GetSelectedEntry(false);
1330 			PwEntry[] vSelected = GetSelectedEntries();
1331 			int iScrollY = NativeMethods.GetScrollPosY(m_lvEntries.Handle);
1332 
1333 			bool bSubEntries = Program.Config.MainWindow.ShowEntriesOfSubGroups;
1334 
1335 			PwGroup pg = (pgSelected ?? GetSelectedGroup());
1336 
1337 			if(bOnlyUpdateCurrentlyShown)
1338 			{
1339 				Debug.Assert(pgSelected == null);
1340 				pg = GetCurrentEntries();
1341 			}
1342 
1343 			PwObjectList<PwEntry> pwlSource = ((pg != null) ?
1344 				pg.GetEntries(bSubEntries) : new PwObjectList<PwEntry>());
1345 
1346 			m_bOnlyTans = ListContainsOnlyTans(pwlSource);
1347 
1348 			m_asyncListUpdate.CancelPendingUpdatesAsync();
1349 
1350 			// https://sourceforge.net/p/keepass/bugs/1698/
1351 			++m_uBlockEntrySelectionEvent;
1352 
1353 			m_lvEntries.BeginUpdate();
1354 			lock(m_asyncListUpdate.ListEditSyncObject)
1355 			{
1356 				m_lvEntries.Items.Clear();
1357 			}
1358 			m_lvEntries.Groups.Clear();
1359 			m_lvgLastEntryGroup = null;
1360 
1361 			// m_bEntryGrouping = (((pg != null) ? pg.IsVirtual : false) || bSubEntries);
1362 			m_bEntryGrouping = bSubEntries;
1363 			if(pg != null)
1364 			{
1365 				PwDatabase pd = m_docMgr.ActiveDatabase;
1366 				if(bOnlyUpdateCurrentlyShown && !m_lvEntries.ShowGroups &&
1367 					EntryUtil.EntriesHaveSameParent(pwlSource) && pd.IsOpen)
1368 				{
1369 					// Just reorder, don't enable grouping
1370 					EntryUtil.ReorderEntriesAsInDatabase(pwlSource, pd);
1371 					peTop = null; // Don't scroll to previous top item
1372 				}
1373 				else m_bEntryGrouping |= pg.IsVirtual;
1374 			}
1375 			int iLg = Program.Config.MainWindow.ListGrouping;
1376 			if((iLg & (int)AceListGrouping.Primary) == (int)AceListGrouping.On)
1377 				m_bEntryGrouping = true;
1378 			else if((iLg & (int)AceListGrouping.Primary) == (int)AceListGrouping.Off)
1379 				m_bEntryGrouping = false;
1380 			m_lvEntries.ShowGroups = m_bEntryGrouping;
1381 
1382 			int nTopIndex = -1;
1383 			ListViewItem lviFocused = null;
1384 
1385 			m_dtCachedNow = DateTime.UtcNow;
1386 			ListViewStateEx lvseCachedState = new ListViewStateEx(m_lvEntries);
1387 
1388 			if(pg != null)
1389 			{
1390 				foreach(PwEntry pe in pwlSource)
1391 				{
1392 					ListViewItem lvi = SetListEntry(pe, null);
1393 
1394 					if(vSelected != null)
1395 					{
1396 						if(Array.IndexOf(vSelected, pe) >= 0)
1397 							lvi.Selected = true;
1398 					}
1399 
1400 					if(pe == peTop) nTopIndex = m_lvEntries.Items.Count - 1;
1401 					if(pe == peFocused) lviFocused = lvi;
1402 				}
1403 			}
1404 
1405 			Debug.Assert(lvseCachedState.CompareTo(m_lvEntries));
1406 
1407 			UIUtil.SetAlternatingBgColors(m_lvEntries, m_clrAlternateItemBgColor,
1408 				Program.Config.MainWindow.EntryListAlternatingBgColors);
1409 
1410 			Debug.Assert(m_bEntryGrouping == m_lvEntries.ShowGroups);
1411 			if(UIUtil.GetGroupsEnabled(m_lvEntries))
1412 			{
1413 				// Test nTopIndex to ensure we're not scrolling an unrelated list
1414 				if((nTopIndex >= 0) && (iScrollY > 0))
1415 					NativeMethods.Scroll(m_lvEntries, 0, iScrollY);
1416 			}
1417 			else
1418 			{
1419 				Debug.Assert((nTopIndex != 0) || (UIUtil.GetTopVisibleItem(
1420 					m_lvEntries) == 0)); // No scrolling required for item 0
1421 				if(nTopIndex > 0)
1422 					UIUtil.SetTopVisibleItem(m_lvEntries, nTopIndex, false);
1423 			}
1424 
1425 			if(lviFocused != null)
1426 				UIUtil.SetFocusedItem(m_lvEntries, lviFocused, false);
1427 
1428 			m_lvEntries.EndUpdate();
1429 			--m_uBlockEntrySelectionEvent;
1430 
1431 			// Switch the view *after* EndUpdate, otherwise drawing bug;
1432 			// https://sourceforge.net/p/keepass/bugs/1651/
1433 			if(m_bSimpleTanView && (m_lvEntries.Items.Count != 0))
1434 				UIUtil.SetView(m_lvEntries, (m_bOnlyTans ? View.List : View.Details));
1435 			else UIUtil.SetView(m_lvEntries, View.Details);
1436 
1437 			// Resize columns *after* EndUpdate, otherwise sizing problem
1438 			// caused by the scrollbar
1439 			if(Program.Config.MainWindow.EntryListAutoResizeColumns &&
1440 				(m_lvEntries.View == View.Details))
1441 				UIUtil.ResizeColumns(m_lvEntries, true);
1442 		}
1443 
1444 		/// <summary>
1445 		/// Refresh the entries list. All currently displayed entries are updated.
1446 		/// If you made changes to the list that change the number of visible entries
1447 		/// (like adding or removing an entry), you must use the <c>UpdateEntryList</c>
1448 		/// function instead.
1449 		/// </summary>
RefreshEntriesList()1450 		public void RefreshEntriesList()
1451 		{
1452 			UpdateImageLists(false); // Important
1453 
1454 			m_lvEntries.BeginUpdate();
1455 			m_dtCachedNow = DateTime.UtcNow;
1456 
1457 			foreach(ListViewItem lvi in m_lvEntries.Items)
1458 			{
1459 				SetListEntry(((PwListItem)lvi.Tag).Entry, lvi);
1460 			}
1461 
1462 			UIUtil.SetAlternatingBgColors(m_lvEntries, m_clrAlternateItemBgColor,
1463 				Program.Config.MainWindow.EntryListAlternatingBgColors);
1464 			m_lvEntries.EndUpdate();
1465 		}
1466 
GetTopEntry()1467 		private PwEntry GetTopEntry()
1468 		{
1469 			PwEntry peTop = null;
1470 			try
1471 			{
1472 				int idxTop = UIUtil.GetTopVisibleItem(m_lvEntries);
1473 				if(idxTop >= 0)
1474 					peTop = ((PwListItem)m_lvEntries.Items[idxTop].Tag).Entry;
1475 			}
1476 			catch(Exception) { Debug.Assert(false); }
1477 
1478 			return peTop;
1479 		}
1480 
SortPasswordList(bool bEnableSorting, int nColumn, SortOrder? soForce, bool bUpdateEntryList)1481 		private void SortPasswordList(bool bEnableSorting, int nColumn,
1482 			SortOrder? soForce, bool bUpdateEntryList)
1483 		{
1484 			AceColumnType colType = GetAceColumn(nColumn).Type;
1485 
1486 			if(bEnableSorting)
1487 			{
1488 				bool bSortTimes = AceColumn.IsTimeColumn(colType);
1489 				bool bSortNaturally = (colType != AceColumnType.Uuid);
1490 
1491 				int nOldColumn = m_pListSorter.Column;
1492 				SortOrder sortOrder = m_pListSorter.Order;
1493 
1494 				if(soForce.HasValue) sortOrder = soForce.Value;
1495 				else if(nColumn == nOldColumn)
1496 				{
1497 					if(sortOrder == SortOrder.None)
1498 						sortOrder = SortOrder.Ascending;
1499 					else if(sortOrder == SortOrder.Ascending)
1500 						sortOrder = SortOrder.Descending;
1501 					else if(sortOrder == SortOrder.Descending)
1502 						sortOrder = SortOrder.None;
1503 					else { Debug.Assert(false); }
1504 				}
1505 				else sortOrder = SortOrder.Ascending;
1506 
1507 				if(sortOrder != SortOrder.None)
1508 				{
1509 					m_pListSorter = new ListSorter(nColumn, sortOrder,
1510 						bSortNaturally, bSortTimes);
1511 					m_lvEntries.ListViewItemSorter = m_pListSorter;
1512 
1513 					// Workaround for XP bug
1514 					if(bUpdateEntryList && !NativeLib.IsUnix() && WinUtil.IsWindowsXP)
1515 						UpdateEntryList(null, true); // Only required on XP
1516 				}
1517 				else
1518 				{
1519 					m_pListSorter = new ListSorter();
1520 					m_lvEntries.ListViewItemSorter = null;
1521 
1522 					if(bUpdateEntryList) UpdateEntryList(null, true);
1523 				}
1524 			}
1525 			else // Disable sorting
1526 			{
1527 				m_pListSorter = new ListSorter();
1528 				m_lvEntries.ListViewItemSorter = null;
1529 
1530 				if(bUpdateEntryList) UpdateEntryList(null, true);
1531 			}
1532 
1533 			UpdateColumnSortingIcons();
1534 			UIUtil.SetAlternatingBgColors(m_lvEntries, m_clrAlternateItemBgColor,
1535 				Program.Config.MainWindow.EntryListAlternatingBgColors);
1536 			UpdateUI(false, null, false, null, false, null, false); // KPB 1134
1537 		}
1538 
UpdateColumnSortingIcons()1539 		private void UpdateColumnSortingIcons()
1540 		{
1541 			if(UIUtil.SetSortIcon(m_lvEntries, m_pListSorter.Column,
1542 				m_pListSorter.Order)) return;
1543 
1544 			// if(m_lvEntries.SmallImageList == null) return;
1545 
1546 			if(m_pListSorter.Column < 0) { Debug.Assert(m_lvEntries.ListViewItemSorter == null); }
1547 
1548 			string strAsc = "  \u2191"; // Must have same length
1549 			string strDsc = "  \u2193"; // Must have same length
1550 			if(WinUtil.IsWindows9x || WinUtil.IsWindows2000 || WinUtil.IsWindowsXP ||
1551 				NativeLib.IsUnix())
1552 			{
1553 				strAsc = @"  ^";
1554 				strDsc = @"  v";
1555 			}
1556 			else if(WinUtil.IsAtLeastWindowsVista)
1557 			{
1558 				strAsc = "  \u25B3";
1559 				strDsc = "  \u25BD";
1560 			}
1561 
1562 			foreach(ColumnHeader ch in m_lvEntries.Columns)
1563 			{
1564 				string strCur = ch.Text, strNew = null;
1565 
1566 				if(strCur.EndsWith(strAsc) || strCur.EndsWith(strDsc))
1567 				{
1568 					strNew = strCur.Substring(0, strCur.Length - strAsc.Length);
1569 					strCur = strNew;
1570 				}
1571 
1572 				if((ch.Index == m_pListSorter.Column) &&
1573 					(m_pListSorter.Order != SortOrder.None))
1574 				{
1575 					if(m_pListSorter.Order == SortOrder.Ascending)
1576 						strNew = strCur + strAsc;
1577 					else if(m_pListSorter.Order == SortOrder.Descending)
1578 						strNew = strCur + strDsc;
1579 				}
1580 
1581 				if(strNew != null) ch.Text = strNew;
1582 			}
1583 		}
1584 
ShowEntryView(bool bShow)1585 		private void ShowEntryView(bool bShow)
1586 		{
1587 			UIUtil.SetChecked(m_menuViewShowEntryView, bShow);
1588 
1589 			Program.Config.MainWindow.EntryView.Show = bShow;
1590 
1591 			m_richEntryView.Visible = bShow;
1592 			m_splitHorizontal.Panel2Collapsed = !bShow;
1593 		}
1594 
ShowEntryDetails(PwEntry pe)1595 		private void ShowEntryDetails(PwEntry pe)
1596 		{
1597 			if(pe == null)
1598 			{
1599 				m_richEntryView.Text = string.Empty;
1600 				m_strLastEntryViewRtf = null;
1601 				return;
1602 			}
1603 
1604 			RichTextBuilder rb = new RichTextBuilder();
1605 
1606 			AceFont af = Program.Config.UI.StandardFont;
1607 			Font fontUI = (UISystemFonts.ListFont ?? m_lvEntries.Font);
1608 			// string strFontFace = (af.OverrideUIDefault ? af.Family : fontUI.Name);
1609 			// float fFontSize = (af.OverrideUIDefault ? af.ToFont().SizeInPoints : fontUI.SizeInPoints);
1610 			if(af.OverrideUIDefault) rb.DefaultFont = af.ToFont();
1611 			else rb.DefaultFont = fontUI;
1612 
1613 			string strItemSeparator = ((m_splitHorizontal.Orientation == Orientation.Horizontal) ?
1614 				", " : Environment.NewLine);
1615 
1616 			List<KeyValuePair<string, bool>> lLinks = new List<KeyValuePair<string, bool>>();
1617 			GAction<string, string> fSetLink = delegate(string strPre, string strLink)
1618 			{
1619 				if(!string.IsNullOrEmpty(strPre))
1620 					lLinks.Add(new KeyValuePair<string, bool>(strPre, false));
1621 				if(!string.IsNullOrEmpty(strLink))
1622 					lLinks.Add(new KeyValuePair<string, bool>(strLink, true));
1623 			};
1624 
1625 			// StringBuilder sb = new StringBuilder();
1626 			// StrUtil.InitRtf(sb, strFontFace, fFontSize);
1627 
1628 			rb.Append(KPRes.Group, FontStyle.Bold, null, null, ":", " ");
1629 			PwGroup pg = pe.ParentGroup;
1630 			if(pg != null)
1631 			{
1632 				rb.Append(pg.Name);
1633 				fSetLink(KPRes.Group + ": ", pg.Name);
1634 			}
1635 
1636 			AceMainWindow mw = Program.Config.MainWindow;
1637 			EvAppendEntryField(rb, strItemSeparator, KPRes.Title,
1638 				mw.IsColumnHidden(AceColumnType.Title) ? PwDefs.HiddenPassword :
1639 				pe.Strings.ReadSafe(PwDefs.TitleField), pe, null);
1640 			EvAppendEntryField(rb, strItemSeparator, KPRes.UserName,
1641 				mw.IsColumnHidden(AceColumnType.UserName) ? PwDefs.HiddenPassword :
1642 				pe.Strings.ReadSafe(PwDefs.UserNameField), pe, null);
1643 			EvAppendEntryField(rb, strItemSeparator, KPRes.Password,
1644 				mw.IsColumnHidden(AceColumnType.Password) ? PwDefs.HiddenPassword :
1645 				pe.Strings.ReadSafe(PwDefs.PasswordField), pe, null);
1646 			EvAppendEntryField(rb, strItemSeparator, KPRes.Url,
1647 				mw.IsColumnHidden(AceColumnType.Url) ? PwDefs.HiddenPassword :
1648 				pe.Strings.ReadSafe(PwDefs.UrlField), pe, fSetLink);
1649 
1650 			if(pe.Expires)
1651 				EvAppendEntryField(rb, strItemSeparator, KPRes.ExpiryTime,
1652 					TimeUtil.ToDisplayString(pe.ExpiryTime), null, null);
1653 
1654 			if(pe.Binaries.UCount != 0)
1655 			{
1656 				EvAppendEntryField(rb, strItemSeparator, KPRes.Attachments,
1657 					pe.Binaries.KeysToString(), null, null);
1658 
1659 				fSetLink(KPRes.Attachments + ": ", null);
1660 				foreach(KeyValuePair<string, ProtectedBinary> kvp in pe.Binaries)
1661 					fSetLink(null, kvp.Key);
1662 			}
1663 
1664 			EvAppendEntryField(rb, strItemSeparator, KPRes.Tags,
1665 				TagUtil.TagsInheritedToString(pe.Tags, pe.ParentGroup), null, null);
1666 			EvAppendEntryField(rb, strItemSeparator, KPRes.UrlOverride,
1667 				pe.OverrideUrl, pe, null);
1668 
1669 			EvAppendEntryField(rb, strItemSeparator, KPRes.CreationTime,
1670 				TimeUtil.ToDisplayString(pe.CreationTime), null, null);
1671 			EvAppendEntryField(rb, strItemSeparator, KPRes.LastModificationTime,
1672 				TimeUtil.ToDisplayString(pe.LastModificationTime), null, null);
1673 			if((Program.Config.UI.UIFlags & (ulong)AceUIFlags.ShowLastAccessTime) != 0)
1674 				EvAppendEntryField(rb, strItemSeparator, KPRes.LastAccessTime,
1675 					TimeUtil.ToDisplayString(pe.LastAccessTime), null, null);
1676 
1677 			foreach(KeyValuePair<string, ProtectedString> kvp in pe.Strings)
1678 			{
1679 				if(PwDefs.IsStandardField(kvp.Key)) continue;
1680 
1681 				string strCustomValue = (mw.ShouldHideCustomString(kvp.Key,
1682 					kvp.Value) ? PwDefs.HiddenPassword : kvp.Value.ReadString());
1683 				EvAppendEntryField(rb, strItemSeparator, kvp.Key, strCustomValue, pe, null);
1684 			}
1685 
1686 			string strNotes = (mw.IsColumnHidden(AceColumnType.Notes) ?
1687 				PwDefs.HiddenPassword : pe.Strings.ReadSafe(PwDefs.NotesField).Trim());
1688 			if(strNotes.Length != 0)
1689 			{
1690 				rb.AppendLine();
1691 				rb.AppendLine();
1692 
1693 				KeyValuePair<string, string> kvpBold = RichTextBuilder.GetStyleIdCodes(
1694 					FontStyle.Bold);
1695 				KeyValuePair<string, string> kvpItalic = RichTextBuilder.GetStyleIdCodes(
1696 					FontStyle.Italic);
1697 				KeyValuePair<string, string> kvpUnderline = RichTextBuilder.GetStyleIdCodes(
1698 					FontStyle.Underline);
1699 
1700 				strNotes = strNotes.Replace(@"<b>", kvpBold.Key);
1701 				strNotes = strNotes.Replace(@"</b>", kvpBold.Value);
1702 				strNotes = strNotes.Replace(@"<i>", kvpItalic.Key);
1703 				strNotes = strNotes.Replace(@"</i>", kvpItalic.Value);
1704 				strNotes = strNotes.Replace(@"<u>", kvpUnderline.Key);
1705 				strNotes = strNotes.Replace(@"</u>", kvpUnderline.Value);
1706 
1707 				rb.Append(strNotes);
1708 			}
1709 
1710 			// sb.Append("\\pard }");
1711 			// m_richEntryView.Rtf = sb.ToString();
1712 
1713 			// If the RTF is the same, do not change the selection/view;
1714 			// https://sourceforge.net/p/keepass/discussion/329220/thread/509843717a/
1715 			if(!rb.Build(m_richEntryView, false, ref m_strLastEntryViewRtf))
1716 				return;
1717 
1718 			UIUtil.RtfLinkifyUrls(m_richEntryView); // Before manual URL link.
1719 			UIUtil.RtfLinkifyReferences(m_richEntryView, false);
1720 			UIUtil.RtfLinkifyTexts(m_richEntryView, lLinks, true); // After auto. URL link.
1721 			m_richEntryView.Select(0, 0);
1722 		}
1723 
EvAppendEntryField(RichTextBuilder rb, string strItemSeparator, string strName, string strRawValue, PwEntry peSprCompile, GAction<string, string> fSetLink)1724 		private void EvAppendEntryField(RichTextBuilder rb,
1725 			string strItemSeparator, string strName, string strRawValue,
1726 			PwEntry peSprCompile, GAction<string, string> fSetLink)
1727 		{
1728 			if(strRawValue == null) { Debug.Assert(false); return; }
1729 
1730 			string strValue = strRawValue.Trim();
1731 			if(strValue.Length == 0) return;
1732 
1733 			rb.Append(strName, FontStyle.Bold, strItemSeparator, null, ":", " ");
1734 
1735 			string strLink = strValue;
1736 
1737 			if((peSprCompile == null) || !SprEngine.MightDeref(strValue))
1738 				rb.Append(strValue);
1739 			else
1740 			{
1741 				string strCmp = SprEngine.Compile(strValue,
1742 					GetEntryListSprContext(peSprCompile,
1743 					m_docMgr.SafeFindContainerOf(peSprCompile)));
1744 				if(strCmp == strValue) rb.Append(strValue);
1745 				else
1746 				{
1747 					strCmp = strCmp.Trim();
1748 
1749 					rb.Append(strCmp);
1750 					rb.Append(" - ");
1751 					rb.Append(strValue, FontStyle.Italic);
1752 
1753 					strLink = strCmp;
1754 				}
1755 			}
1756 
1757 			if((fSetLink != null) && (strLink != PwDefs.HiddenPassword))
1758 				fSetLink(strName + ": ", strLink);
1759 		}
1760 
PerformDefaultAction(object sender, EventArgs e, PwEntry pe, int colID)1761 		private void PerformDefaultAction(object sender, EventArgs e, PwEntry pe,
1762 			int colID)
1763 		{
1764 			if(pe == null) { Debug.Assert(false); return; }
1765 
1766 			if(this.DefaultEntryAction != null)
1767 			{
1768 				CancelEntryEventArgs args = new CancelEntryEventArgs(pe, colID);
1769 				this.DefaultEntryAction(sender, args);
1770 				if(args.Cancel) return;
1771 			}
1772 
1773 			bool bCnt = false;
1774 
1775 			AceColumn col = GetAceColumn(colID);
1776 			AceColumnType colType = col.Type;
1777 			switch(colType)
1778 			{
1779 				case AceColumnType.Title:
1780 					if(PwDefs.IsTanEntry(pe)) OnEntryCopyPassword(sender, e);
1781 					else OnEntryEdit(sender, e);
1782 					break;
1783 				case AceColumnType.UserName:
1784 					OnEntryCopyUserName(sender, e);
1785 					break;
1786 				case AceColumnType.Password:
1787 					OnEntryCopyPassword(sender, e);
1788 					break;
1789 				case AceColumnType.Url:
1790 					PerformDefaultUrlAction(null, null);
1791 					break;
1792 				case AceColumnType.Notes:
1793 					bCnt = ClipboardUtil.CopyAndMinimize(pe.Strings.GetSafe(
1794 						PwDefs.NotesField), true, this, pe, m_docMgr.ActiveDatabase);
1795 					break;
1796 				case AceColumnType.CreationTime:
1797 					bCnt = ClipboardUtil.CopyAndMinimize(TimeUtil.ToDisplayString(
1798 						pe.CreationTime), true, this, pe, null);
1799 					break;
1800 				case AceColumnType.LastModificationTime:
1801 					bCnt = ClipboardUtil.CopyAndMinimize(TimeUtil.ToDisplayString(
1802 						pe.LastModificationTime), true, this, pe, null);
1803 					break;
1804 				case AceColumnType.LastAccessTime:
1805 					bCnt = ClipboardUtil.CopyAndMinimize(TimeUtil.ToDisplayString(
1806 						pe.LastAccessTime), true, this, pe, null);
1807 					break;
1808 				case AceColumnType.ExpiryTime:
1809 					if(pe.Expires)
1810 						bCnt = ClipboardUtil.CopyAndMinimize(TimeUtil.ToDisplayString(
1811 							pe.ExpiryTime), true, this, pe, null);
1812 					else
1813 						bCnt = ClipboardUtil.CopyAndMinimize(m_strNeverExpires,
1814 							true, this, pe, null);
1815 					break;
1816 				case AceColumnType.Attachment:
1817 				case AceColumnType.AttachmentCount:
1818 					PerformDefaultAttachmentAction();
1819 					break;
1820 				case AceColumnType.Uuid:
1821 					bCnt = ClipboardUtil.CopyAndMinimize(pe.Uuid.ToHexString(),
1822 						true, this, pe, null);
1823 					break;
1824 				case AceColumnType.CustomString:
1825 					bCnt = ClipboardUtil.CopyAndMinimize(pe.Strings.ReadSafe(
1826 						col.CustomName), true, this, pe, m_docMgr.ActiveDatabase);
1827 					break;
1828 				case AceColumnType.PluginExt:
1829 					if(Program.ColumnProviderPool.SupportsCellAction(col.CustomName))
1830 						Program.ColumnProviderPool.PerformCellAction(col.CustomName, pe);
1831 					else
1832 						bCnt = ClipboardUtil.CopyAndMinimize(
1833 							Program.ColumnProviderPool.GetCellData(col.CustomName, pe),
1834 							true, this, pe, m_docMgr.ActiveDatabase);
1835 					break;
1836 				case AceColumnType.OverrideUrl:
1837 					bCnt = ClipboardUtil.CopyAndMinimize(pe.OverrideUrl,
1838 						true, this, pe, null);
1839 					break;
1840 				case AceColumnType.Tags:
1841 					bCnt = ClipboardUtil.CopyAndMinimize(TagUtil.TagsInheritedToString(
1842 						pe.Tags, pe.ParentGroup), true, this, pe, null);
1843 					break;
1844 				case AceColumnType.ExpiryTimeDateOnly:
1845 					if(pe.Expires)
1846 						bCnt = ClipboardUtil.CopyAndMinimize(TimeUtil.ToDisplayStringDateOnly(
1847 							pe.ExpiryTime), true, this, pe, null);
1848 					else
1849 						bCnt = ClipboardUtil.CopyAndMinimize(m_strNeverExpires,
1850 							true, this, pe, null);
1851 					break;
1852 				case AceColumnType.Size:
1853 					bCnt = ClipboardUtil.CopyAndMinimize(StrUtil.FormatDataSizeKB(
1854 						pe.GetSize()), true, this, pe, null);
1855 					break;
1856 				case AceColumnType.HistoryCount:
1857 					EditSelectedEntry(PwEntryFormTab.History);
1858 					break;
1859 				case AceColumnType.LastPasswordModTime:
1860 					bCnt = ClipboardUtil.CopyAndMinimize(TimeUtil.ToDisplayString(
1861 						EntryUtil.GetLastPasswordModTime(pe)), true, this, pe, null);
1862 					break;
1863 				case AceColumnType.AutoTypeEnabled:
1864 					object oBlocker;
1865 					AutoType.GetEnabledText(pe, out oBlocker);
1866 					if((oBlocker == null) || (oBlocker is PwEntry))
1867 						EditSelectedEntry(PwEntryFormTab.AutoType);
1868 					else
1869 					{
1870 						PwGroup pg = (oBlocker as PwGroup);
1871 						if(pg != null)
1872 						{
1873 							UpdateUI(false, null, true, pg, true, null, false);
1874 							EditSelectedGroup(GroupFormTab.AutoType);
1875 						}
1876 						else { Debug.Assert(false); }
1877 					}
1878 					break;
1879 				case AceColumnType.AutoTypeSequences:
1880 					EditSelectedEntry(PwEntryFormTab.AutoType);
1881 					break;
1882 				default:
1883 					Debug.Assert(false);
1884 					break;
1885 			}
1886 
1887 			if(bCnt) StartClipboardCountdown();
1888 		}
1889 
1890 		// obForceOpen true: open, false: copy, null: by option and UI
PerformDefaultUrlAction(PwEntry[] vOptEntries, bool? obForceOpen)1891 		internal void PerformDefaultUrlAction(PwEntry[] vOptEntries, bool? obForceOpen)
1892 		{
1893 			PwEntry[] v = (vOptEntries ?? GetSelectedEntries());
1894 			if((v == null) || (v.Length == 0)) { Debug.Assert(false); return; }
1895 
1896 			bool bCopy = Program.Config.MainWindow.CopyUrlsInsteadOfOpening;
1897 			if(obForceOpen.HasValue) bCopy = !obForceOpen.Value;
1898 			else if((Control.ModifierKeys & Keys.Shift) != Keys.None)
1899 				bCopy = !bCopy;
1900 
1901 			if(bCopy)
1902 			{
1903 				if(ClipboardUtil.CopyAndMinimize(UrlsToString(v, true), true, this, null, null))
1904 					StartClipboardCountdown();
1905 			}
1906 			else // Open
1907 			{
1908 				foreach(PwEntry pe in v) WinUtil.OpenEntryUrl(pe);
1909 			}
1910 		}
1911 
PerformDefaultAttachmentAction()1912 		private void PerformDefaultAttachmentAction()
1913 		{
1914 			PwEntry pe = GetSelectedEntry(false);
1915 			if(pe == null) return;
1916 
1917 			foreach(KeyValuePair<string, ProtectedBinary> kvp in pe.Binaries)
1918 			{
1919 				ExecuteBinaryOpen(pe, kvp.Key);
1920 				break;
1921 			}
1922 		}
1923 
ExecuteBinaryOpen(PwEntry pe, string strBinName)1924 		private void ExecuteBinaryOpen(PwEntry pe, string strBinName)
1925 		{
1926 			EntryBinaryDataContext ctx = new EntryBinaryDataContext();
1927 			ctx.Entry = pe;
1928 			ctx.Name = strBinName;
1929 
1930 			DynamicMenuEventArgs args = new DynamicMenuEventArgs(strBinName, ctx);
1931 			OnEntryBinaryOpen(null, args);
1932 		}
1933 
UrlsToString(PwEntry[] vEntries, bool bActive)1934 		private string UrlsToString(PwEntry[] vEntries, bool bActive)
1935 		{
1936 			if((vEntries == null) || (vEntries.Length == 0)) return string.Empty;
1937 
1938 			StringBuilder sb = new StringBuilder();
1939 			foreach(PwEntry pe in vEntries)
1940 			{
1941 				if(sb.Length > 0) sb.Append(MessageService.NewLine);
1942 
1943 				PwDatabase pd = m_docMgr.SafeFindContainerOf(pe);
1944 
1945 				string strUrl = pe.Strings.ReadSafe(PwDefs.UrlField);
1946 				strUrl = SprEngine.Compile(strUrl, new SprContext(pe, pd,
1947 					(bActive ? SprCompileFlags.All : SprCompileFlags.NonActive)));
1948 
1949 				sb.Append(strUrl);
1950 			}
1951 
1952 			UpdateUIState(false); // SprEngine.Compile might have modified the database
1953 			return sb.ToString();
1954 		}
1955 
PerformSearchQuickDelegate(string strSearch, bool bForceShowExpired, bool bRespectEntrySearchingDisabled)1956 		private delegate void PerformSearchQuickDelegate(string strSearch,
1957 			bool bForceShowExpired, bool bRespectEntrySearchingDisabled);
1958 
PerformSearchQuick(string strSearch, bool bForceShowExpired, bool bRespectEntrySearchingDisabled)1959 		private void PerformSearchQuick(string strSearch, bool bForceShowExpired,
1960 			bool bRespectEntrySearchingDisabled)
1961 		{
1962 			if(strSearch == null) { Debug.Assert(false); strSearch = string.Empty; }
1963 
1964 			SearchParameters sp = new SearchParameters();
1965 
1966 			if(strSearch.StartsWith("//") && strSearch.EndsWith("//") &&
1967 				(strSearch.Length > 4))
1968 			{
1969 				sp.SearchString = strSearch.Substring(2, strSearch.Length - 4);
1970 				sp.SearchMode = PwSearchMode.Regular;
1971 			}
1972 			else sp.SearchString = strSearch;
1973 
1974 			sp.SearchInPasswords = Program.Config.MainWindow.QuickFindSearchInPasswords;
1975 			sp.SearchInTags = sp.SearchInUuids = sp.SearchInGroupPaths =
1976 				sp.SearchInGroupNames = true;
1977 
1978 			sp.ExcludeExpired = (!bForceShowExpired &&
1979 				Program.Config.MainWindow.QuickFindExcludeExpired);
1980 			sp.RespectEntrySearchingDisabled = bRespectEntrySearchingDisabled;
1981 
1982 			SearchUtil.SetTransformation(sp, (Program.Config.MainWindow.QuickFindDerefData ?
1983 				SearchUtil.StrTrfDeref : string.Empty));
1984 
1985 			PerformSearch(sp, false, Program.Config.MainWindow.FocusResultsAfterQuickFind);
1986 		}
1987 
ShowExpiredEntries(bool bOnlyIfExists, bool bShowExpired, int iExpInDays, bool bInMonths30, bool bFocusEntryList)1988 		private void ShowExpiredEntries(bool bOnlyIfExists, bool bShowExpired,
1989 			int iExpInDays, bool bInMonths30, bool bFocusEntryList)
1990 		{
1991 			if(!bShowExpired && (iExpInDays <= 0)) return;
1992 			Debug.Assert(iExpInDays >= 0); // 0 = do not show expiring soon
1993 
1994 			PwDatabase pd = m_docMgr.ActiveDatabase;
1995 			if((pd == null) || !pd.IsOpen || (pd.RootGroup == null)) return;
1996 
1997 			PwGroup pgResults = new PwGroup(true, true, string.Empty, PwIcon.Expired);
1998 			pgResults.IsVirtual = true;
1999 
2000 			bool bExpInP = bShowExpired; // Past
2001 			bool bExpInF = (iExpInDays == int.MaxValue); // Future
2002 			bool bExpInD = ((iExpInDays > 0) && !bExpInF);
2003 
2004 			DateTime dtNow = DateTime.UtcNow;
2005 			DateTime dtLimit = dtNow;
2006 			if(bExpInD)
2007 			{
2008 				try
2009 				{
2010 					if(bInMonths30 && ((iExpInDays % 30) == 0))
2011 						dtLimit = dtNow.AddMonths(iExpInDays / 30);
2012 					else
2013 					{
2014 						Debug.Assert(!bInMonths30);
2015 						dtLimit = dtNow.AddDays(iExpInDays);
2016 					}
2017 
2018 					dtLimit = TimeUtil.ToLocal(dtLimit, false);
2019 					dtLimit = dtLimit.Date.Add(new TimeSpan(23, 59, 59));
2020 					dtLimit = TimeUtil.ToUtc(dtLimit, false);
2021 				}
2022 				catch(Exception) { Debug.Assert(false); }
2023 			}
2024 
2025 			EntryHandler eh = delegate(PwEntry pe)
2026 			{
2027 				if(!pe.Expires) return true;
2028 				if(!pe.GetSearchingEnabled()) return true;
2029 				if(PwDefs.IsTanEntry(pe)) return true; // Exclude TANs
2030 
2031 				int iRelNow = pe.ExpiryTime.CompareTo(dtNow);
2032 
2033 				if((bExpInP && (iRelNow <= 0)) ||
2034 					(bExpInD && (pe.ExpiryTime <= dtLimit) && (iRelNow > 0)) ||
2035 					(bExpInF && (iRelNow > 0)))
2036 					pgResults.AddEntry(pe, false, false);
2037 				return true;
2038 			};
2039 
2040 			pd.RootGroup.TraverseTree(TraversalMethod.PreOrder, null, eh);
2041 
2042 			if((pgResults.Entries.UCount != 0) || !bOnlyIfExists)
2043 			{
2044 				SearchParameters sp = new SearchParameters();
2045 				sp.RespectEntrySearchingDisabled = true;
2046 
2047 				ShowSearchResults(pgResults, sp, pd.RootGroup, bFocusEntryList);
2048 			}
2049 		}
2050 
PerformExport(PwGroup pgDataSource, bool bExportDeleted)2051 		public void PerformExport(PwGroup pgDataSource, bool bExportDeleted)
2052 		{
2053 			PwDatabase pd = m_docMgr.ActiveDatabase;
2054 			if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; }
2055 
2056 			if(!AppPolicy.Try(AppPolicyId.Export)) return;
2057 
2058 			PwGroup pg = (pgDataSource ?? pd.RootGroup);
2059 			PwExportInfo pwInfo = new PwExportInfo(pg, pd, bExportDeleted);
2060 
2061 			MessageService.ExternalIncrementMessageCount();
2062 			ShowWarningsLogger swLogger = CreateShowWarningsLogger();
2063 
2064 			ExportUtil.Export(pwInfo, swLogger);
2065 
2066 			MessageService.ExternalDecrementMessageCount();
2067 			UpdateUIState(false);
2068 		}
2069 
CompleteConnectionInfo(IOConnectionInfo ioc, bool bSave, bool bCanRememberCred, bool bTestConnection, bool bForceShow)2070 		internal static IOConnectionInfo CompleteConnectionInfo(IOConnectionInfo ioc,
2071 			bool bSave, bool bCanRememberCred, bool bTestConnection, bool bForceShow)
2072 		{
2073 			if(ioc == null) { Debug.Assert(false); return null; }
2074 			if(!bForceShow && ((ioc.CredSaveMode == IOCredSaveMode.SaveCred) ||
2075 				ioc.IsLocalFile() || ioc.IsComplete))
2076 				return ioc.CloneDeep();
2077 
2078 			IOConnectionForm dlg = new IOConnectionForm();
2079 			dlg.InitEx(bSave, ioc, bCanRememberCred, bTestConnection);
2080 			if(UIUtil.ShowDialogNotValue(dlg, DialogResult.OK)) return null;
2081 
2082 			IOConnectionInfo iocResult = dlg.IOConnectionInfo;
2083 			UIUtil.DestroyForm(dlg);
2084 			return iocResult;
2085 		}
2086 
CompleteConnectionInfoUsingMru(IOConnectionInfo ioc)2087 		internal IOConnectionInfo CompleteConnectionInfoUsingMru(IOConnectionInfo ioc)
2088 		{
2089 			if(ioc == null) { Debug.Assert(false); return null; }
2090 			if(ioc.Password.Length > 0) return ioc;
2091 
2092 			for(uint u = 0; u < m_mruList.ItemCount; ++u)
2093 			{
2094 				IOConnectionInfo iocMru = (m_mruList.GetItem(u).Value as IOConnectionInfo);
2095 				if(iocMru == null) { Debug.Assert(false); continue; }
2096 
2097 				if(iocMru.Path.Equals(ioc.Path, StrUtil.CaseIgnoreCmp))
2098 				{
2099 					if((ioc.UserName.Length > 0) && !ioc.UserName.Equals(
2100 						iocMru.UserName, StrUtil.CaseIgnoreCmp))
2101 						continue;
2102 
2103 					return iocMru.CloneDeep();
2104 				}
2105 			}
2106 
2107 			return ioc;
2108 		}
2109 
2110 		private sealed class OdKpfConstructParams
2111 		{
2112 			public IOConnectionInfo IOConnectionInfo = null;
2113 			public bool CanExit = false;
2114 			public bool SecureDesktopMode = false; // Must be false by default
2115 		}
2116 
OdKpfConstruct(object objParam)2117 		private static Form OdKpfConstruct(object objParam)
2118 		{
2119 			OdKpfConstructParams p = (objParam as OdKpfConstructParams);
2120 			if(p == null) { Debug.Assert(false); return null; }
2121 
2122 			KeyPromptForm kpf = new KeyPromptForm();
2123 			kpf.InitEx(p.IOConnectionInfo, p.CanExit, true);
2124 			kpf.SecureDesktopMode = p.SecureDesktopMode;
2125 			return kpf;
2126 		}
2127 
2128 		private sealed class OdKpfResult
2129 		{
2130 			public CompositeKey Key = null;
2131 			public bool ShowHelpAfterClose = false;
2132 			public bool HasClosedWithExit = false;
2133 		}
2134 
OdKpfBuildResult(Form f)2135 		private static object OdKpfBuildResult(Form f)
2136 		{
2137 			KeyPromptForm kpf = (f as KeyPromptForm);
2138 			if(kpf == null) { Debug.Assert(false); return null; }
2139 
2140 			OdKpfResult kpfResult = new OdKpfResult();
2141 			kpfResult.Key = kpf.CompositeKey;
2142 			kpfResult.ShowHelpAfterClose = kpf.ShowHelpAfterClose;
2143 			kpfResult.HasClosedWithExit = kpf.HasClosedWithExit;
2144 
2145 			return kpfResult;
2146 		}
2147 
2148 		/// <summary>
2149 		/// Open a database. This function opens the specified database and
2150 		/// updates the user interface.
2151 		/// </summary>
OpenDatabase(IOConnectionInfo ioConnection, CompositeKey cmpKey, bool bOpenLocal)2152 		public void OpenDatabase(IOConnectionInfo ioConnection, CompositeKey cmpKey,
2153 			bool bOpenLocal)
2154 		{
2155 			if(!IsCommandTypeInvokable(null, AppCommandType.Window)) { Debug.Assert(false); return; }
2156 
2157 			if((ioConnection != null) && (ioConnection.Path.Length > 0) &&
2158 				ioConnection.IsLocalFile() && !UrlUtil.IsAbsolutePath(ioConnection.Path))
2159 			{
2160 				ioConnection = ioConnection.CloneDeep();
2161 				ioConnection.Path = UrlUtil.MakeAbsolutePath(WinUtil.GetExecutable(),
2162 					ioConnection.Path);
2163 			}
2164 
2165 			if(!m_bFormLoaded && Program.Config.Application.Start.MinimizedAndLocked &&
2166 				(ioConnection != null) && (ioConnection.Path.Length > 0))
2167 			{
2168 				PwDocument ds = m_docMgr.CreateNewDocument(true);
2169 				ds.LockedIoc = ioConnection.CloneDeep();
2170 				UpdateUI(true, ds, true, null, true, null, false);
2171 				return;
2172 			}
2173 
2174 			SaveWindowState(); // KPF 1093
2175 
2176 			IOConnectionInfo ioc;
2177 			if(ioConnection == null)
2178 			{
2179 				if(bOpenLocal)
2180 				{
2181 					OpenFileDialogEx ofdDb = UIUtil.CreateOpenFileDialog(KPRes.OpenDatabaseFile,
2182 						UIUtil.CreateFileTypeFilter(AppDefs.FileExtension.FileExt,
2183 						KPRes.KdbxFiles, true), 1, null, false,
2184 						AppDefs.FileDialogContext.Database);
2185 
2186 					GlobalWindowManager.AddDialog(ofdDb.FileDialog);
2187 					DialogResult dr = ofdDb.ShowDialog();
2188 					GlobalWindowManager.RemoveDialog(ofdDb.FileDialog);
2189 					if(dr != DialogResult.OK) return;
2190 
2191 					ioc = IOConnectionInfo.FromPath(ofdDb.FileName);
2192 				}
2193 				else
2194 				{
2195 					ioc = CompleteConnectionInfo(new IOConnectionInfo(), false,
2196 						true, true, true);
2197 					if(ioc == null) return;
2198 				}
2199 			}
2200 			else // ioConnection != null
2201 			{
2202 				ioc = CompleteConnectionInfo(ioConnection, false, true, true, false);
2203 				if(ioc == null) return;
2204 			}
2205 
2206 			if(!ioc.CanProbablyAccess())
2207 			{
2208 				MessageService.ShowWarning(ioc.GetDisplayName(), KPRes.FileNotFoundError);
2209 				return;
2210 			}
2211 
2212 			if(OpenDatabaseRestoreIfOpened(ioc)) return;
2213 
2214 			PwDatabase pwOpenedDb = null;
2215 			bool bAbort;
2216 			if(cmpKey == null)
2217 			{
2218 				for(int iTry = 0; iTry < Program.Config.Security.MasterKeyTries; ++iTry)
2219 				{
2220 					OdKpfConstructParams kpfParams = new OdKpfConstructParams();
2221 					kpfParams.IOConnectionInfo = ioc;
2222 					kpfParams.CanExit = IsFileLocked(null);
2223 
2224 					DialogResult dr;
2225 					OdKpfResult kpfResult;
2226 
2227 					if(Program.Config.Security.MasterKeyOnSecureDesktop &&
2228 						WinUtil.IsAtLeastWindows2000 && !NativeLib.IsUnix())
2229 					{
2230 						kpfParams.SecureDesktopMode = true;
2231 
2232 						ProtectedDialog dlg = new ProtectedDialog(OdKpfConstruct,
2233 							OdKpfBuildResult);
2234 						object objResult;
2235 						dr = dlg.ShowDialog(out objResult, kpfParams);
2236 						if(dr == DialogResult.None) { Debug.Assert(false); dr = DialogResult.Cancel; }
2237 
2238 						kpfResult = (objResult as OdKpfResult);
2239 						if(kpfResult == null) { Debug.Assert(false); continue; }
2240 
2241 						if(kpfResult.ShowHelpAfterClose)
2242 							AppHelp.ShowHelp(AppDefs.HelpTopics.KeySources, null);
2243 					}
2244 					else // Show dialog on normal desktop
2245 					{
2246 						Form dlg = OdKpfConstruct(kpfParams);
2247 						dr = dlg.ShowDialog();
2248 
2249 						kpfResult = (OdKpfBuildResult(dlg) as OdKpfResult);
2250 						UIUtil.DestroyForm(dlg);
2251 						if(kpfResult == null) { Debug.Assert(false); continue; }
2252 					}
2253 
2254 					if(dr == DialogResult.Cancel) break;
2255 					else if(kpfResult.HasClosedWithExit)
2256 					{
2257 						Debug.Assert(dr == DialogResult.Abort);
2258 						OnFileExit(null, null);
2259 						return;
2260 					}
2261 
2262 					pwOpenedDb = OpenDatabaseInternal(ioc, kpfResult.Key,
2263 						out bAbort);
2264 					if((pwOpenedDb != null) || bAbort) break;
2265 				}
2266 			}
2267 			else // cmpKey != null
2268 			{
2269 				pwOpenedDb = OpenDatabaseInternal(ioc, cmpKey, out bAbort);
2270 			}
2271 
2272 			if((pwOpenedDb == null) || !pwOpenedDb.IsOpen)
2273 			{
2274 				UpdateUIState(false); // Reset status bar text
2275 				return;
2276 			}
2277 
2278 			string strName = pwOpenedDb.IOConnectionInfo.GetDisplayName();
2279 			m_mruList.AddItem(strName, pwOpenedDb.IOConnectionInfo.CloneDeep());
2280 
2281 			PwDocument dsExisting = m_docMgr.FindDocument(pwOpenedDb);
2282 			if(dsExisting != null) m_docMgr.ActiveDocument = dsExisting;
2283 
2284 			bool bCorrectDbActive = (m_docMgr.ActiveDocument.Database == pwOpenedDb);
2285 			Debug.Assert(bCorrectDbActive);
2286 
2287 			AutoEnableVisualHiding(true);
2288 
2289 			// SetLastUsedFile(pwOpenedDb.IOConnectionInfo);
2290 			RememberKeySources(pwOpenedDb);
2291 
2292 			PwGroup pgRestoreSelect = null;
2293 			if(bCorrectDbActive)
2294 			{
2295 				m_docMgr.ActiveDocument.LockedIoc = new IOConnectionInfo(); // Clear
2296 
2297 				pgRestoreSelect = pwOpenedDb.RootGroup.FindGroup(
2298 					pwOpenedDb.LastSelectedGroup, true);
2299 			}
2300 
2301 			UpdateUI(true, null, true, pgRestoreSelect, true, null, false);
2302 			if(bCorrectDbActive)
2303 			{
2304 				SetTopVisibleGroup(pwOpenedDb.LastTopVisibleGroup);
2305 				if(pgRestoreSelect != null)
2306 					SetTopVisibleEntry(pgRestoreSelect.LastTopVisibleEntry);
2307 			}
2308 			UpdateColumnSortingIcons();
2309 
2310 			long lMkDays = (DateTime.UtcNow - pwOpenedDb.MasterKeyChanged).Days;
2311 			bool bMkChangeForce = ((pwOpenedDb.MasterKeyChangeForce >= 0) &&
2312 				(lMkDays >= pwOpenedDb.MasterKeyChangeForce));
2313 			bMkChangeForce |= pwOpenedDb.MasterKeyChangeForceOnce;
2314 
2315 			bool bMkChangeRec = ((pwOpenedDb.MasterKeyChangeRec >= 0) &&
2316 				(lMkDays >= pwOpenedDb.MasterKeyChangeRec));
2317 			string strMkExpiry = (Program.Config.Security.MasterKeyExpiryRec ??
2318 				string.Empty).Trim();
2319 			if(strMkExpiry.Length != 0)
2320 			{
2321 				try
2322 				{
2323 					if(strMkExpiry.StartsWith("P", StrUtil.CaseIgnoreCmp) ||
2324 						strMkExpiry.StartsWith("-P", StrUtil.CaseIgnoreCmp))
2325 					{
2326 						TimeSpan tsExpiry = XmlConvert.ToTimeSpan(strMkExpiry);
2327 						bMkChangeRec |= ((pwOpenedDb.MasterKeyChanged +
2328 							tsExpiry) < DateTime.UtcNow);
2329 					}
2330 					else
2331 					{
2332 						DateTime dtExpiry = XmlConvert.ToDateTime(strMkExpiry,
2333 							XmlDateTimeSerializationMode.Utc);
2334 						bMkChangeRec |= (pwOpenedDb.MasterKeyChanged < dtExpiry);
2335 					}
2336 				}
2337 				catch(Exception exMkExp)
2338 				{
2339 					MessageService.ShowWarning("Configuration/Security/MasterKeyExpiryRec:",
2340 						exMkExp);
2341 				}
2342 			}
2343 
2344 			if(bMkChangeForce)
2345 			{
2346 				// Updating the UI might trigger an auto-save
2347 				pwOpenedDb.MasterKeyChangeForceOnce = false;
2348 
2349 				while(true)
2350 				{
2351 					MessageService.ShowInfo(pwOpenedDb.IOConnectionInfo.GetDisplayName() +
2352 						MessageService.NewParagraph + KPRes.MasterKeyChangeForce +
2353 						MessageService.NewParagraph + KPRes.MasterKeyChangeInfo);
2354 					if(ChangeMasterKey(pwOpenedDb)) break;
2355 					if(!AppPolicy.Current.ChangeMasterKey) break; // Prevent endless loop
2356 				}
2357 			}
2358 			else if(bMkChangeRec)
2359 			{
2360 				if(MessageService.AskYesNo(pwOpenedDb.IOConnectionInfo.GetDisplayName() +
2361 					MessageService.NewParagraph + KPRes.MasterKeyChangeRec +
2362 					MessageService.NewParagraph + KPRes.MasterKeyChangeQ))
2363 					ChangeMasterKey(pwOpenedDb);
2364 			}
2365 
2366 			if(FixDuplicateUuids(pwOpenedDb, pwOpenedDb.IOConnectionInfo))
2367 				UpdateUIState(false); // Already marked as modified
2368 
2369 			if(this.FileOpened != null)
2370 			{
2371 				FileOpenedEventArgs ea = new FileOpenedEventArgs(pwOpenedDb);
2372 				this.FileOpened(this, ea);
2373 			}
2374 			Program.TriggerSystem.RaiseEvent(EcasEventIDs.OpenedDatabaseFile,
2375 				EcasProperty.Database, pwOpenedDb);
2376 
2377 			if(bCorrectDbActive && pwOpenedDb.IsOpen)
2378 			{
2379 				ShowExpiredEntries(true,
2380 					Program.Config.Application.FileOpening.ShowExpiredEntries,
2381 					(Program.Config.Application.FileOpening.ShowSoonToExpireEntries ?
2382 					Program.Config.Application.ExpirySoonDays : 0), false, false);
2383 
2384 				// Avoid view being destroyed by the unlocking routine
2385 				pwOpenedDb.LastSelectedGroup = PwUuid.Zero;
2386 			}
2387 
2388 			if(Program.Config.MainWindow.MinimizeAfterOpeningDatabase)
2389 				UIUtil.SetWindowState(this, FormWindowState.Minimized);
2390 
2391 			ResetDefaultFocus(null);
2392 		}
2393 
OpenDatabaseInternal(IOConnectionInfo ioc, CompositeKey cmpKey, out bool bAbort)2394 		private PwDatabase OpenDatabaseInternal(IOConnectionInfo ioc,
2395 			CompositeKey cmpKey, out bool bAbort)
2396 		{
2397 			bAbort = false;
2398 
2399 			PerformSelfTest();
2400 
2401 			PwDocument ds = null;
2402 			string strPathNrm = ioc.Path.Trim().ToLower();
2403 			for(int iScan = 0; iScan < m_docMgr.Documents.Count; ++iScan)
2404 			{
2405 				if(m_docMgr.Documents[iScan].LockedIoc.Path.Trim().ToLower() == strPathNrm)
2406 					ds = m_docMgr.Documents[iScan];
2407 				else if(m_docMgr.Documents[iScan].Database.IOConnectionInfo.Path == strPathNrm)
2408 					ds = m_docMgr.Documents[iScan];
2409 			}
2410 
2411 			PwDatabase pwDb;
2412 			if(ds == null) pwDb = m_docMgr.CreateNewDocument(true).Database;
2413 			else pwDb = ds.Database;
2414 
2415 			UIBlockInteraction(true);
2416 			ShowWarningsLogger swLogger = CreateShowWarningsLogger();
2417 			swLogger.StartLogging(KPRes.OpeningDatabase2, true);
2418 			m_sCancellable.Push(swLogger);
2419 
2420 			Exception ex = null;
2421 			try
2422 			{
2423 				pwDb.Open(ioc, cmpKey, swLogger);
2424 
2425 #if DEBUG
2426 				byte[] pbDiskDirect = WinUtil.HashFile(ioc);
2427 				Debug.Assert(MemUtil.ArraysEqual(pbDiskDirect, pwDb.HashOfFileOnDisk));
2428 #endif
2429 			}
2430 			catch(Exception exLoad)
2431 			{
2432 				ex = exLoad;
2433 				pwDb = null;
2434 			}
2435 
2436 			m_sCancellable.Pop();
2437 			swLogger.EndLogging();
2438 			UIBlockInteraction(false);
2439 
2440 			if(pwDb == null)
2441 			{
2442 				if(ds == null) m_docMgr.CloseDatabase(m_docMgr.ActiveDatabase);
2443 			}
2444 
2445 			if(ex != null)
2446 			{
2447 				string strMsg = MessageService.GetLoadWarningMessage(
2448 					ioc.GetDisplayName(), ex,
2449 					(Program.CommandLineArgs[AppDefs.CommandLineOptions.Debug] != null));
2450 
2451 				bool bShowStd = true;
2452 				if(!ioc.IsLocalFile())
2453 				{
2454 					VistaTaskDialog vtd = new VistaTaskDialog();
2455 					vtd.CommandLinks = false;
2456 					vtd.Content = strMsg;
2457 					vtd.DefaultButtonID = (int)DialogResult.Cancel;
2458 					// vtd.VerificationText = KPRes.CredSpecifyDifferent;
2459 					vtd.WindowTitle = PwDefs.ShortProductName;
2460 
2461 					vtd.SetIcon(VtdIcon.Warning);
2462 					vtd.AddButton((int)DialogResult.Cancel, KPRes.Ok, null);
2463 					vtd.AddButton((int)DialogResult.Retry,
2464 						KPRes.CredSpecifyDifferent, null);
2465 
2466 					if(vtd.ShowDialog(this))
2467 					{
2468 						bShowStd = false;
2469 
2470 						// if(vtd.ResultVerificationChecked)
2471 						if(vtd.Result == (int)DialogResult.Retry)
2472 						{
2473 							IOConnectionInfo iocNew = ioc.CloneDeep();
2474 							// iocNew.ClearCredentials(false);
2475 							iocNew.CredSaveMode = IOCredSaveMode.NoSave;
2476 							iocNew.IsComplete = false;
2477 							// iocNew.Password = string.Empty;
2478 
2479 							OpenDatabase(iocNew, null, false);
2480 
2481 							bAbort = true;
2482 						}
2483 					}
2484 				}
2485 
2486 				if(bShowStd) MessageService.ShowWarning(strMsg);
2487 				// MessageService.ShowLoadWarning(ioc.GetDisplayName(), ex,
2488 				//	(Program.CommandLineArgs[AppDefs.CommandLineOptions.Debug] != null));
2489 			}
2490 
2491 			return pwDb;
2492 		}
2493 
OpenDatabaseRestoreIfOpened(IOConnectionInfo ioc)2494 		private bool OpenDatabaseRestoreIfOpened(IOConnectionInfo ioc)
2495 		{
2496 			if(ioc == null) { Debug.Assert(false); return false; }
2497 
2498 			string strPathNrm = ioc.Path.Trim().ToLower();
2499 
2500 			foreach(PwDocument ds in m_docMgr.Documents)
2501 			{
2502 				if(((ds.LockedIoc == null) || (ds.LockedIoc.Path.Length == 0)) &&
2503 					(ds.Database.IOConnectionInfo.Path.Trim().ToLower() == strPathNrm))
2504 				{
2505 					MakeDocumentActive(ds);
2506 					return true;
2507 				}
2508 			}
2509 
2510 			return false;
2511 		}
2512 
AutoEnableVisualHiding(bool bNewOpenDatabase)2513 		private static void AutoEnableVisualHiding(bool bNewOpenDatabase)
2514 		{
2515 			// // KPF 1802197
2516 			// // Turn on visual hiding if option is selected
2517 			// if(m_docMgr.ActiveDatabase.MemoryProtection.AutoEnableVisualHiding)
2518 			// {
2519 			//	if(m_docMgr.ActiveDatabase.MemoryProtection.ProtectTitle && !m_viewHideFields.ProtectTitle)
2520 			//		m_menuViewHideTitles.Checked = m_viewHideFields.ProtectTitle = true;
2521 			//	if(m_docMgr.ActiveDatabase.MemoryProtection.ProtectUserName && !m_viewHideFields.ProtectUserName)
2522 			//		m_menuViewHideUserNames.Checked = m_viewHideFields.ProtectUserName = true;
2523 			//	if(m_docMgr.ActiveDatabase.MemoryProtection.ProtectPassword && !m_viewHideFields.ProtectPassword)
2524 			//		m_menuViewHidePasswords.Checked = m_viewHideFields.ProtectPassword = true;
2525 			//	if(m_docMgr.ActiveDatabase.MemoryProtection.ProtectUrl && !m_viewHideFields.ProtectUrl)
2526 			//		m_menuViewHideURLs.Checked = m_viewHideFields.ProtectUrl = true;
2527 			//	if(m_docMgr.ActiveDatabase.MemoryProtection.ProtectNotes && !m_viewHideFields.ProtectNotes)
2528 			//		m_menuViewHideNotes.Checked = m_viewHideFields.ProtectNotes = true;
2529 			// }
2530 
2531 			if(bNewOpenDatabase && !Program.Config.UI.Hiding.RememberHidingPasswordsMain)
2532 			{
2533 				List<AceColumn> l = Program.Config.MainWindow.FindColumns(
2534 					AceColumnType.Password);
2535 				foreach(AceColumn c in l) c.HideWithAsterisks = true; // Reset
2536 			}
2537 		}
2538 
GuiFindGroup(PwUuid puSearch, TreeNode tnContainer)2539 		private TreeNode GuiFindGroup(PwUuid puSearch, TreeNode tnContainer)
2540 		{
2541 			if(puSearch == null) { Debug.Assert(false); return null; }
2542 
2543 			if(tnContainer == null)
2544 			{
2545 				if(m_tvGroups.Nodes.Count == 0) return null;
2546 				tnContainer = m_tvGroups.Nodes[0];
2547 			}
2548 
2549 			PwGroup pg = (tnContainer.Tag as PwGroup);
2550 			if(pg != null)
2551 			{
2552 				if(pg.Uuid.Equals(puSearch)) return tnContainer;
2553 			}
2554 			else { Debug.Assert(false); }
2555 
2556 			foreach(TreeNode tn in tnContainer.Nodes)
2557 			{
2558 				if(tn != tnContainer)
2559 				{
2560 					TreeNode tnRet = GuiFindGroup(puSearch, tn);
2561 					if(tnRet != null) return tnRet;
2562 				}
2563 				else { Debug.Assert(false); }
2564 			}
2565 
2566 			return null;
2567 		}
2568 
GuiFindEntry(PwUuid puSearch)2569 		private ListViewItem GuiFindEntry(PwUuid puSearch)
2570 		{
2571 			Debug.Assert(puSearch != null);
2572 			if(puSearch == null) return null;
2573 
2574 			foreach(ListViewItem lvi in m_lvEntries.Items)
2575 			{
2576 				if(((PwListItem)lvi.Tag).Entry.Uuid.Equals(puSearch))
2577 					return lvi;
2578 			}
2579 
2580 			return null;
2581 		}
2582 
PrintGroup(PwGroup pg)2583 		private void PrintGroup(PwGroup pg)
2584 		{
2585 			if(pg == null) { Debug.Assert(false); return; }
2586 
2587 			PwDatabase pd = m_docMgr.ActiveDatabase;
2588 			if((pd == null) || !pd.IsOpen) return;
2589 
2590 			if(!AppPolicy.Try(AppPolicyId.Print)) return;
2591 			if(!AppPolicy.Current.PrintNoKey)
2592 			{
2593 				if(!KeyUtil.ReAskKey(pd, true)) return;
2594 			}
2595 
2596 			// Printing selected entries should not always reveal parent
2597 			// group information (group notes, ...)
2598 			// pg = ExportUtil.WithParentGroups(pg, pd);
2599 
2600 			// Changing the root group of the database breaks Spr compilation
2601 			// PwGroup pgOrgRoot = pd.RootGroup;
2602 			// pd.RootGroup = pg;
2603 
2604 			PrintForm pf = new PrintForm();
2605 			pf.InitEx(pg, pd, m_ilCurrentIcons, true, m_pListSorter.Column);
2606 			UIUtil.ShowDialogAndDestroy(pf);
2607 
2608 			// pd.RootGroup = pgOrgRoot;
2609 		}
2610 
OnMruExecute(string strDisplayName, object oTag, ToolStripMenuItem tsmiParent)2611 		public void OnMruExecute(string strDisplayName, object oTag,
2612 			ToolStripMenuItem tsmiParent)
2613 		{
2614 			if(tsmiParent == null) { Debug.Assert(false); return; }
2615 
2616 			IOConnectionInfo ioc = (oTag as IOConnectionInfo);
2617 			if(ioc == null) { Debug.Assert(false); return; }
2618 
2619 			if(tsmiParent == m_menuFileRecent)
2620 				OpenDatabase(ioc, null, false);
2621 			else if(tsmiParent == m_menuFileSyncRecent)
2622 			{
2623 				PwDatabase pd = m_docMgr.ActiveDatabase;
2624 				if(!pd.IsOpen) return;
2625 
2626 				ioc = CompleteConnectionInfo(ioc, false, true, true, false);
2627 				if(ioc == null) return;
2628 
2629 				bool? b = ImportUtil.Synchronize(pd, this, ioc, false, this);
2630 				UpdateUI(false, null, true, null, true, null, false);
2631 				if(b.HasValue) SetStatusEx(b.Value ? KPRes.SyncSuccess : KPRes.SyncFailed);
2632 			}
2633 			else { Debug.Assert(false); }
2634 		}
2635 
OnMruClear()2636 		public void OnMruClear()
2637 		{
2638 			m_mruList.Clear();
2639 		}
2640 
2641 		// For plugins
UpdateTrayIcon()2642 		public void UpdateTrayIcon()
2643 		{
2644 			UpdateTrayIcon(true);
2645 		}
2646 
UpdateTrayIcon(bool bRefreshIcon)2647 		private void UpdateTrayIcon(bool bRefreshIcon)
2648 		{
2649 			if(m_ntfTray == null) { Debug.Assert(false); return; } // Required
2650 
2651 			bool bWindowVisible = this.Visible;
2652 			bool bTrayVisible = m_ntfTray.Visible;
2653 
2654 			if(Program.Config.UI.TrayIcon.ShowOnlyIfTrayedEx)
2655 				m_ntfTray.Visible = !bWindowVisible;
2656 			else if(bWindowVisible && !bTrayVisible)
2657 				m_ntfTray.Visible = true;
2658 
2659 			if(bRefreshIcon) m_ntfTray.RefreshShellIcon();
2660 		}
2661 
OnSessionLock(object sender, SessionLockEventArgs e)2662 		private void OnSessionLock(object sender, SessionLockEventArgs e)
2663 		{
2664 			// Shutdown is handled through OnFormClosing (the default
2665 			// WndProc of the Form class tries to close the window when
2666 			// receiving WM_QUERYENDSESSION or WM_ENDSESSION)
2667 
2668 			if((e.Reason == SessionLockReason.RemoteControlChange) &&
2669 				Program.Config.Security.WorkspaceLocking.LockOnRemoteControlChange) { }
2670 			else if(((e.Reason == SessionLockReason.Lock) ||
2671 				(e.Reason == SessionLockReason.Ending) ||
2672 				(e.Reason == SessionLockReason.UserSwitch)) &&
2673 				Program.Config.Security.WorkspaceLocking.LockOnSessionSwitch) { }
2674 			else if((e.Reason == SessionLockReason.Suspend) &&
2675 				Program.Config.Security.WorkspaceLocking.LockOnSuspend) { }
2676 			else return;
2677 
2678 			if(IsAtLeastOneFileOpen()) LockAllDocuments();
2679 		}
2680 
2681 		/// <summary>
2682 		/// Reset the internal user inactivity timers.
2683 		/// </summary>
NotifyUserActivity()2684 		public void NotifyUserActivity()
2685 		{
2686 			// m_nLockTimerCur = m_nLockTimerMax;
2687 
2688 			if(m_nLockTimerMax == 0) m_lLockAtTicks = long.MaxValue;
2689 			else
2690 			{
2691 				DateTime utcLockAt = DateTime.UtcNow;
2692 				utcLockAt = utcLockAt.AddSeconds((double)m_nLockTimerMax);
2693 				m_lLockAtTicks = utcLockAt.Ticks;
2694 			}
2695 
2696 			Program.TriggerSystem.NotifyUserActivity();
2697 
2698 			if(this.UserActivityPost != null)
2699 				this.UserActivityPost(null, EventArgs.Empty);
2700 		}
2701 
UpdateGlobalLockTimeout(DateTime utcNow)2702 		private void UpdateGlobalLockTimeout(DateTime utcNow)
2703 		{
2704 			uint uLockGlobal = Program.Config.Security.WorkspaceLocking.LockAfterGlobalTime;
2705 			if(uLockGlobal == 0) { m_lLockAtGlobalTicks = long.MaxValue; return; }
2706 
2707 			uint? uLastInputTime = NativeMethods.GetLastInputTime();
2708 			if(!uLastInputTime.HasValue) return;
2709 
2710 			if(uLastInputTime.Value != m_uLastInputTime)
2711 			{
2712 				DateTime utcLockAt = utcNow.AddSeconds((double)uLockGlobal);
2713 				m_lLockAtGlobalTicks = utcLockAt.Ticks;
2714 
2715 				m_uLastInputTime = uLastInputTime.Value;
2716 			}
2717 		}
2718 
2719 		/// <summary>
2720 		/// Move selected entries.
2721 		/// </summary>
2722 		/// <param name="iMove">Must be -2/2 to move to top/bottom, -1/1 to
2723 		/// move one up/down.</param>
MoveSelectedEntries(int iMove)2724 		private void MoveSelectedEntries(int iMove)
2725 		{
2726 			// Don't allow moving when sorting is enabled
2727 			if((m_pListSorter.Column >= 0) && (m_pListSorter.Order != SortOrder.None))
2728 				return;
2729 
2730 			PwEntry[] vEntries = GetSelectedEntries();
2731 			Debug.Assert(vEntries != null); if(vEntries == null) return;
2732 			Debug.Assert(vEntries.Length > 0); if(vEntries.Length == 0) return;
2733 
2734 			PwGroup pg = vEntries[0].ParentGroup;
2735 			if(pg == null) { Debug.Assert(false); return; }
2736 			foreach(PwEntry pe in vEntries)
2737 			{
2738 				if(pe.ParentGroup != pg)
2739 				{
2740 					MessageService.ShowWarning(KPRes.CannotMoveEntriesBcsGroup);
2741 					return;
2742 				}
2743 			}
2744 
2745 			if((iMove == -1) || (iMove == 1))
2746 				pg.Entries.MoveOne(vEntries, (iMove < 0));
2747 			else if((iMove == -2) || (iMove == 2))
2748 				pg.Entries.MoveTopBottom(vEntries, (iMove < 0));
2749 			else { Debug.Assert(false); return; }
2750 
2751 			DateTime dtNow = DateTime.UtcNow;
2752 			foreach(PwEntry peUpd in vEntries) peUpd.LocationChanged = dtNow;
2753 
2754 			// Blocking prevents correct scrolling when groups are enabled
2755 			// and the scroll position is near the end
2756 			// m_lvEntries.BeginUpdate();
2757 
2758 			bool bScrollOne = ((Math.Abs(iMove) == 1) &&
2759 				!UIUtil.GetGroupsEnabled(m_lvEntries));
2760 
2761 			int iTop = UIUtil.GetTopVisibleItem(m_lvEntries);
2762 			ListView.SelectedIndexCollection lvsic = m_lvEntries.SelectedIndices;
2763 			if(lvsic.Count > 0)
2764 			{
2765 				int pCrit = lvsic[(iMove < 0) ? 0 : (lvsic.Count - 1)];
2766 
2767 				if(iMove < 0)
2768 				{
2769 					if(pCrit <= iTop) bScrollOne = false;
2770 					else if((pCrit - iTop) <= 3) { } // Auto-scroll
2771 					else bScrollOne = false;
2772 				}
2773 				else // iMove > 0
2774 				{
2775 					int nVisible = UIUtil.GetMaxVisibleItemCount(m_lvEntries);
2776 					if(pCrit < (iTop + nVisible - 4)) bScrollOne = false;
2777 				}
2778 			}
2779 			else { Debug.Assert(false); bScrollOne = false; }
2780 
2781 			if(bScrollOne)
2782 			{
2783 				// if(UIUtil.GetGroupsEnabled(m_lvEntries))
2784 				// {
2785 				//	if(m_lvEntries.Items.Count > 0)
2786 				//	{
2787 				//		int dy = m_lvEntries.Items[0].Bounds.Height;
2788 				//		if(dy > 1)
2789 				//			NativeMethods.Scroll(m_lvEntries, 0,
2790 				//				iMove * (dy + (dy / 4))); // With spacing added
2791 				//	}
2792 				// }
2793 
2794 				// if(!UIUtil.GetGroupsEnabled(m_lvEntries))
2795 				// {
2796 				iTop += iMove;
2797 				iTop = Math.Max(Math.Min(iTop, m_lvEntries.Items.Count - 1), 0);
2798 				UIUtil.SetTopVisibleItem(m_lvEntries, iTop, false);
2799 				// }
2800 			}
2801 
2802 			UpdateEntryList(null, false);
2803 			EnsureVisibleSelected(iMove > 0); // In all cases
2804 
2805 			// m_lvEntries.EndUpdate();
2806 			UpdateUIState(true);
2807 		}
2808 
CreateStatusBarLogger()2809 		public StatusBarLogger CreateStatusBarLogger()
2810 		{
2811 			StatusBarLogger sl = new StatusBarLogger();
2812 			sl.SetControls(m_statusPartInfo, m_statusPartProgress);
2813 			return sl;
2814 		}
2815 
2816 		/// <summary>
2817 		/// Create a new warnings logger object that logs directly into
2818 		/// the main status bar until the first warning is shown (in that
2819 		/// case a dialog is opened displaying the warning).
2820 		/// </summary>
2821 		/// <returns>Reference to the new logger object.</returns>
CreateShowWarningsLogger()2822 		public ShowWarningsLogger CreateShowWarningsLogger()
2823 		{
2824 			StatusBarLogger sl = CreateStatusBarLogger();
2825 			return new ShowWarningsLogger(sl, this);
2826 		}
2827 
HandleHotKey(int wParam)2828 		internal void HandleHotKey(int wParam)
2829 		{
2830 			if(HotKeyManager.HandleHotKeyIntoSelf(wParam)) return;
2831 
2832 			if(wParam == AppDefs.GlobalHotKeyId.AutoType)
2833 				ExecuteGlobalAutoType();
2834 			else if(wParam == AppDefs.GlobalHotKeyId.AutoTypePassword)
2835 				ExecuteGlobalAutoType(@"{PASSWORD}");
2836 			else if(wParam == AppDefs.GlobalHotKeyId.AutoTypeSelected)
2837 				ExecuteEntryAutoType();
2838 			else if(wParam == AppDefs.GlobalHotKeyId.ShowWindow)
2839 			{
2840 				bool bWndVisible = ((this.WindowState != FormWindowState.Minimized) &&
2841 					!IsTrayed());
2842 				EnsureVisibleForegroundWindow(true, true);
2843 				if(bWndVisible && IsFileLocked(null))
2844 					OnFileLock(null, EventArgs.Empty); // Unlock
2845 			}
2846 			else if(wParam == AppDefs.GlobalHotKeyId.EntryMenu)
2847 				EntryMenu.Show();
2848 			else { Debug.Assert(false); }
2849 		}
2850 
WndProc(ref Message m)2851 		protected override void WndProc(ref Message m)
2852 		{
2853 			if(m.Msg == NativeMethods.WM_HOTKEY)
2854 			{
2855 				NotifyUserActivity();
2856 				HandleHotKey((int)m.WParam);
2857 			}
2858 			else if((m.Msg == m_nAppMessage) && (m_nAppMessage != 0))
2859 				ProcessAppMessage(m.WParam, m.LParam);
2860 			else if(m.Msg == NativeMethods.WM_SYSCOMMAND)
2861 			{
2862 				if((m.WParam == (IntPtr)NativeMethods.SC_MINIMIZE) ||
2863 					(m.WParam == (IntPtr)NativeMethods.SC_MAXIMIZE))
2864 				{
2865 					SaveWindowPositionAndSize();
2866 				}
2867 			}
2868 			else if(m.Msg == NativeMethods.WM_SETTINGCHANGE)
2869 				UIUtil.OnSystemSettingChange();
2870 			else if((m.Msg == NativeMethods.WM_POWERBROADCAST) &&
2871 				((m.WParam == (IntPtr)NativeMethods.PBT_APMQUERYSUSPEND) ||
2872 				(m.WParam == (IntPtr)NativeMethods.PBT_APMSUSPEND)))
2873 			{
2874 				OnSessionLock(null, new SessionLockEventArgs(SessionLockReason.Suspend));
2875 			}
2876 			else if((m.Msg == m_nTaskbarButtonMessage) && m_bTaskbarButtonMessage)
2877 			{
2878 				m_bTaskbarButtonMessage = false;
2879 				UpdateUIState(false, null); // Set overlay icon
2880 				m_bTaskbarButtonMessage = true;
2881 			}
2882 			else if(m.Msg == DwmUtil.WM_DWMSENDICONICTHUMBNAIL)
2883 			{
2884 				DwmUtil.SetIconicThumbnail(this, AppIcons.Default, ref m);
2885 				return;
2886 			}
2887 			else if(m.Msg == DwmUtil.WM_DWMSENDICONICLIVEPREVIEWBITMAP)
2888 			{
2889 				DwmUtil.SetIconicPreview(this, AppIcons.Default, ref m);
2890 				return;
2891 			}
2892 
2893 			base.WndProc(ref m);
2894 		}
2895 
ProcessAppMessage(IntPtr wParam, IntPtr lParam)2896 		public void ProcessAppMessage(IntPtr wParam, IntPtr lParam)
2897 		{
2898 			NotifyUserActivity();
2899 
2900 			switch(wParam.ToInt64())
2901 			{
2902 				case (long)Program.AppMessage.RestoreWindow:
2903 					EnsureVisibleForegroundWindow(true, true);
2904 					break;
2905 				case (long)Program.AppMessage.Exit:
2906 					OnFileExit(null, EventArgs.Empty);
2907 					break;
2908 				case (long)Program.AppMessage.IpcByFile:
2909 					IpcUtilEx.ProcessGlobalMessage((int)lParam.ToInt64(), this, false);
2910 					break;
2911 				case (long)Program.AppMessage.AutoType:
2912 					ExecuteGlobalAutoType();
2913 					break;
2914 				case (long)Program.AppMessage.Lock:
2915 					LockAllDocuments();
2916 					break;
2917 				case (long)Program.AppMessage.Unlock:
2918 					if(IsFileLocked(null))
2919 					{
2920 						EnsureVisibleForegroundWindow(false, false);
2921 						OnFileLock(null, EventArgs.Empty); // Unlock
2922 					}
2923 					break;
2924 				case (long)Program.AppMessage.AutoTypeSelected:
2925 					ExecuteEntryAutoType();
2926 					break;
2927 				case (long)Program.AppMessage.Cancel:
2928 					OnTrayCancel(null, EventArgs.Empty);
2929 					break;
2930 				case (long)Program.AppMessage.AutoTypePassword:
2931 					ExecuteGlobalAutoType(@"{PASSWORD}");
2932 					break;
2933 				case (long)Program.AppMessage.IpcByFile1:
2934 					IpcUtilEx.ProcessGlobalMessage((int)lParam.ToInt64(), this, true);
2935 					break;
2936 				default:
2937 					Debug.Assert(false);
2938 					break;
2939 			}
2940 		}
2941 
ExecuteGlobalAutoType()2942 		public void ExecuteGlobalAutoType()
2943 		{
2944 			ExecuteGlobalAutoType(null);
2945 		}
2946 
ExecuteGlobalAutoType(string strSeq)2947 		private void ExecuteGlobalAutoType(string strSeq)
2948 		{
2949 			if(m_bIsAutoTyping) return;
2950 			m_bIsAutoTyping = true;
2951 
2952 			if(!IsAtLeastOneFileOpen())
2953 			{
2954 				try
2955 				{
2956 					IntPtr hPrevWnd = NativeMethods.GetForegroundWindowHandle();
2957 
2958 					EnsureVisibleForegroundWindow(false, false);
2959 
2960 					if(!IsCommandTypeInvokable(null, AppCommandType.Lock))
2961 					{
2962 						m_bIsAutoTyping = false;
2963 						return;
2964 					}
2965 
2966 					// The window restoration function above maybe
2967 					// restored the window already, therefore only
2968 					// try to unlock if it's locked *now*
2969 					if(IsFileLocked(null))
2970 					{
2971 						// https://sourceforge.net/p/keepass/bugs/1163/
2972 						bool bFirst = true;
2973 						EventHandler<GwmWindowEventArgs> eh = delegate(object sender,
2974 							GwmWindowEventArgs e)
2975 						{
2976 							if(!bFirst) return;
2977 							bFirst = false;
2978 							GlobalWindowManager.ActivateTopWindow();
2979 						};
2980 						GlobalWindowManager.WindowAdded += eh;
2981 
2982 						OnFileLock(null, EventArgs.Empty);
2983 
2984 						GlobalWindowManager.WindowAdded -= eh;
2985 					}
2986 
2987 					NativeMethods.EnsureForegroundWindow(hPrevWnd);
2988 				}
2989 				catch(Exception exAT)
2990 				{
2991 					MessageService.ShowWarning(exAT);
2992 				}
2993 			}
2994 			if(!IsAtLeastOneFileOpen()) { m_bIsAutoTyping = false; return; }
2995 
2996 			try
2997 			{
2998 				AutoType.PerformGlobal(m_docMgr.GetOpenDatabases(),
2999 					m_ilCurrentIcons, strSeq);
3000 			}
3001 			catch(Exception exGlobal)
3002 			{
3003 				MessageService.ShowWarning(exGlobal);
3004 			}
3005 
3006 			m_bIsAutoTyping = false;
3007 		}
3008 
ExecuteEntryAutoType()3009 		private void ExecuteEntryAutoType()
3010 		{
3011 			try
3012 			{
3013 				IntPtr hFG = NativeMethods.GetForegroundWindowHandle();
3014 				if(!AutoType.IsValidAutoTypeWindow(hFG, true)) return;
3015 			}
3016 			catch(Exception) { Debug.Assert(false); return; }
3017 
3018 			PwEntry peSel = GetSelectedEntry(true);
3019 			if(peSel != null)
3020 				AutoType.PerformIntoCurrentWindow(peSel,
3021 					m_docMgr.SafeFindContainerOf(peSel));
3022 			else
3023 			{
3024 				EnsureVisibleForegroundWindow(true, true);
3025 				MessageService.ShowWarning(KPRes.AutoTypeSelectedNoEntry,
3026 					KPRes.AutoTypeGlobalHint);
3027 			}
3028 		}
3029 
EnsureVisibleForegroundWindow(bool bUntray, bool bRestoreWindow)3030 		public void EnsureVisibleForegroundWindow(bool bUntray, bool bRestoreWindow)
3031 		{
3032 			if(GlobalWindowManager.ActivateTopWindow()) return;
3033 
3034 			if(bUntray && IsTrayed()) MinimizeToTray(false);
3035 
3036 			if(bRestoreWindow && (this.WindowState == FormWindowState.Minimized))
3037 				UIUtil.SetWindowState(this, FormWindowState.Normal);
3038 
3039 			UIUtil.EnsureInsideScreen(this);
3040 
3041 			try
3042 			{
3043 				if(this.Visible) // && (this.WindowState != FormWindowState.Minimized)
3044 				{
3045 					this.BringToFront();
3046 					this.Activate();
3047 				}
3048 			}
3049 			catch(Exception) { Debug.Assert(false); }
3050 		}
3051 
SetListFont(AceFont font)3052 		private void SetListFont(AceFont font)
3053 		{
3054 			if((font != null) && font.OverrideUIDefault)
3055 			{
3056 				m_tvGroups.Font = font.ToFont();
3057 				m_lvEntries.Font = font.ToFont();
3058 				m_richEntryView.Font = font.ToFont();
3059 
3060 				Program.Config.UI.StandardFont = font;
3061 			}
3062 			else
3063 			{
3064 				if(UIUtil.VistaStyleListsSupported)
3065 				{
3066 					Font fontUI = UISystemFonts.ListFont;
3067 					m_tvGroups.Font = fontUI;
3068 					m_lvEntries.Font = fontUI;
3069 					m_richEntryView.Font = fontUI;
3070 				}
3071 
3072 				Program.Config.UI.StandardFont.OverrideUIDefault = false;
3073 			}
3074 
3075 			m_fontExpired = FontUtil.CreateFont(m_lvEntries.Font, FontStyle.Strikeout);
3076 			m_fontBoldUI = FontUtil.CreateFont(m_tabMain.Font, FontStyle.Bold);
3077 			m_fontBoldTree = FontUtil.CreateFont(m_lvEntries.Font, FontStyle.Bold);
3078 			m_fontItalicTree = FontUtil.CreateFont(m_lvEntries.Font, FontStyle.Italic);
3079 		}
3080 
SetSelectedEntryColor(Color clrBack)3081 		private void SetSelectedEntryColor(Color clrBack)
3082 		{
3083 			PwDatabase pd = m_docMgr.ActiveDatabase;
3084 			if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; }
3085 
3086 			PwEntry[] vSelected = GetSelectedEntries();
3087 			if((vSelected == null) || (vSelected.Length == 0)) return;
3088 
3089 			bool bMod = false;
3090 			foreach(PwEntry pe in vSelected)
3091 			{
3092 				if(UIUtil.ColorsEqual(pe.BackgroundColor, clrBack)) continue;
3093 
3094 				pe.CreateBackup(pd);
3095 				pe.BackgroundColor = clrBack;
3096 				pe.Touch(true, false);
3097 
3098 				bMod = true;
3099 			}
3100 
3101 			SelectEntries(new PwObjectList<PwEntry>(), true, false); // Ensure color visible
3102 			RefreshEntriesList();
3103 			UpdateUIState(bMod);
3104 		}
3105 
OnCopyCustomString(object sender, DynamicMenuEventArgs e)3106 		private void OnCopyCustomString(object sender, DynamicMenuEventArgs e)
3107 		{
3108 			string strKey = (e.Tag as string);
3109 			if(string.IsNullOrEmpty(strKey)) { Debug.Assert(false); return; }
3110 
3111 			PwEntry pe = GetSelectedEntry(false);
3112 			if(pe == null) { Debug.Assert(false); return; }
3113 
3114 			if(ClipboardUtil.CopyAndMinimize(pe.Strings.GetSafe(strKey), true,
3115 				this, pe, m_docMgr.ActiveDatabase))
3116 				StartClipboardCountdown();
3117 		}
3118 
SetMainWindowLayout(bool bSideBySide)3119 		private void SetMainWindowLayout(bool bSideBySide)
3120 		{
3121 			if(!bSideBySide && (m_splitHorizontal.Orientation != Orientation.Horizontal))
3122 			{
3123 				m_splitHorizontal.Orientation = Orientation.Horizontal;
3124 				UpdateUIState(false);
3125 			}
3126 			else if(bSideBySide && (m_splitHorizontal.Orientation != Orientation.Vertical))
3127 			{
3128 				m_splitHorizontal.Orientation = Orientation.Vertical;
3129 				UpdateUIState(false);
3130 			}
3131 
3132 			UIUtil.SetChecked(m_menuViewWindowsStacked, !bSideBySide);
3133 			UIUtil.SetChecked(m_menuViewWindowsSideBySide, bSideBySide);
3134 		}
3135 
AssignMenuShortcuts()3136 		private void AssignMenuShortcuts()
3137 		{
3138 			bool bMoveMono = MonoWorkarounds.IsRequired(1245);
3139 			Keys kMoveMod = (bMoveMono ? (Keys.Control | Keys.Shift) : Keys.Alt);
3140 
3141 			UIUtil.AssignShortcut(m_menuFileNew, Keys.Control | Keys.N);
3142 			UIUtil.AssignShortcut(m_menuFileOpenLocal, Keys.Control | Keys.O);
3143 			UIUtil.AssignShortcut(m_menuFileOpenUrl, Keys.Control | Keys.Shift | Keys.O);
3144 			UIUtil.AssignShortcut(m_menuFileClose, Keys.Control | Keys.W);
3145 			UIUtil.AssignShortcut(m_menuFileSave, Keys.Control | Keys.S);
3146 			UIUtil.AssignShortcut(m_menuFilePrintDatabase, Keys.Control | Keys.P);
3147 			UIUtil.AssignShortcut(m_menuFileSyncFile, Keys.Control | Keys.R);
3148 			UIUtil.AssignShortcut(m_menuFileSyncUrl, Keys.Control | Keys.Shift | Keys.R);
3149 			UIUtil.AssignShortcut(m_menuFileLock, Keys.Control | Keys.L);
3150 			UIUtil.AssignShortcut(m_menuFileExit, Keys.Control | Keys.Q);
3151 
3152 			UIUtil.AssignShortcut(m_menuGroupDelete, Keys.Delete, null, true);
3153 
3154 			UIUtil.AssignShortcut(m_menuGroupMoveToTop, (bMoveMono ?
3155 				Keys.F5 : Keys.Home) | kMoveMod, null, true);
3156 			UIUtil.AssignShortcut(m_menuGroupMoveOneUp, (bMoveMono ?
3157 				Keys.F6 : Keys.Up) | kMoveMod, null, true);
3158 			UIUtil.AssignShortcut(m_menuGroupMoveOneDown, (bMoveMono ?
3159 				Keys.F7 : Keys.Down) | kMoveMod, null, true);
3160 			UIUtil.AssignShortcut(m_menuGroupMoveToBottom, (bMoveMono ?
3161 				Keys.F8 : Keys.End) | kMoveMod, null, true);
3162 			// UIUtil.AssignShortcut(m_menuGroupSort, Keys.Control | Keys.Decimal);
3163 			// UIUtil.AssignShortcut(m_menuGroupSortRec, Keys.Control | Keys.Shift | Keys.Decimal); // (Control+)Shift+Decimal = Delete
3164 			UIUtil.AssignShortcut(m_menuGroupExpand, Keys.Control | Keys.Multiply);
3165 			UIUtil.AssignShortcut(m_menuGroupCollapse, Keys.Control | Keys.Divide);
3166 
3167 			UIUtil.AssignShortcut(m_menuGroupPrint, Keys.Control | Keys.Shift | Keys.P);
3168 
3169 			UIUtil.AssignShortcut(m_menuEntryCopyUserName, Keys.Control | Keys.B);
3170 			UIUtil.AssignShortcut(m_menuEntryCopyPassword, Keys.Control | Keys.C,
3171 				null, true);
3172 			UIUtil.AssignShortcut(m_menuEntryOpenUrl, Keys.Control | Keys.U);
3173 			UIUtil.AssignShortcut(m_menuEntryCopyUrl, Keys.Control | Keys.Shift | Keys.U);
3174 			UIUtil.AssignShortcut(m_menuEntryPerformAutoType, Keys.Control | Keys.V,
3175 				null, true);
3176 
3177 			UIUtil.AssignShortcut(m_menuEntryAdd, Keys.Control | Keys.I);
3178 			UIUtil.AssignShortcut(m_menuEntryEdit, Keys.Return, null, true);
3179 			UIUtil.AssignShortcut(m_menuEntryDuplicate, Keys.Control | Keys.K);
3180 			UIUtil.AssignShortcut(m_menuEntryDelete, Keys.Delete, null, true);
3181 
3182 			UIUtil.AssignShortcut(m_menuEntrySelectAll, Keys.Control | Keys.A,
3183 				null, true);
3184 
3185 			UIUtil.AssignShortcut(m_menuEntryMoveToTop, (bMoveMono ?
3186 				Keys.F5 : Keys.Home) | kMoveMod, null, true);
3187 			UIUtil.AssignShortcut(m_menuEntryMoveOneUp, (bMoveMono ?
3188 				Keys.F6 : Keys.Up) | kMoveMod, null, true);
3189 			UIUtil.AssignShortcut(m_menuEntryMoveOneDown, (bMoveMono ?
3190 				Keys.F7 : Keys.Down) | kMoveMod, null, true);
3191 			UIUtil.AssignShortcut(m_menuEntryMoveToBottom, (bMoveMono ?
3192 				Keys.F8 : Keys.End) | kMoveMod, null, true);
3193 
3194 			UIUtil.AssignShortcut(m_menuEntryClipCopy, Keys.Control | Keys.Shift | Keys.C,
3195 				null, true);
3196 			UIUtil.AssignShortcut(m_menuEntryClipPaste, Keys.Control | Keys.Shift | Keys.V,
3197 				null, true);
3198 
3199 			UIUtil.AssignShortcut(m_menuFindInDatabase, Keys.Control | Keys.F);
3200 			UIUtil.AssignShortcut(m_menuFindInGroup, Keys.Control | Keys.Shift | Keys.F,
3201 				m_ctxGroupFind, false);
3202 
3203 			UIUtil.AssignShortcut(m_menuFindParentGroup, Keys.Control | Keys.G);
3204 
3205 			// UIUtil.AssignShortcut(m_menuViewHidePasswords, Keys.Control | Keys.H);
3206 			// UIUtil.AssignShortcut(m_menuViewHideUserNames, Keys.Control | Keys.J);
3207 
3208 			UIUtil.AssignShortcut(m_menuHelpContents, Keys.F1);
3209 		}
3210 
ConstructContextMenus()3211 		private void ConstructContextMenus()
3212 		{
3213 			m_ctxGroupList.SuspendLayout();
3214 			m_ctxPwList.SuspendLayout();
3215 
3216 			ToolStripItemCollection tsicGC = m_ctxGroupList.Items;
3217 			tsicGC.Insert(0, new ToolStripSeparator());
3218 			m_milMain.CreateCopy(tsicGC, null, false, m_menuGroupRearrange);
3219 			tsicGC.Insert(0, new ToolStripSeparator());
3220 			m_milMain.CreateCopy(tsicGC, null, false, m_menuGroupEmptyRB);
3221 			m_milMain.CreateCopy(tsicGC, null, false, m_menuGroupDelete);
3222 			m_milMain.CreateCopy(tsicGC, null, false, m_menuGroupDuplicate);
3223 			m_milMain.CreateCopy(tsicGC, null, false, m_menuGroupEdit);
3224 			m_milMain.CreateCopy(tsicGC, null, false, m_menuGroupAdd);
3225 			// tsicGC.Add(new ToolStripSeparator());
3226 			// m_milMain.CreateCopy(tsicGC, null, true, m_menuGroupPrint);
3227 			// m_milMain.CreateCopy(tsicGC, null, true, m_menuGroupExport);
3228 
3229 			ToolStripItemCollection tsicEC = m_ctxPwList.Items;
3230 			m_milMain.CreateCopy(tsicEC, null, false, m_menuEntryCopyPassword);
3231 			m_milMain.CreateCopy(tsicEC, null, false, m_menuEntryCopyUserName);
3232 			m_milMain.CreateLink(m_ctxEntryUrl, m_menuEntryUrl, false);
3233 
3234 			ToolStripItemCollection tsicECUrl = m_ctxEntryUrl.DropDownItems;
3235 			m_milMain.CreateCopy(tsicECUrl, null, true, m_menuEntryOpenUrl);
3236 			m_milMain.CreateCopy(tsicECUrl, null, true, m_menuEntryCopyUrl);
3237 
3238 			m_milMain.CreateLink(m_ctxEntryCopyString, m_menuEntryCopyString, false);
3239 			m_milMain.CreateLink(m_ctxEntryAttachments, m_menuEntryAttachments, false);
3240 			m_milMain.CreateCopy(tsicEC, m_ctxEntryAttachments, true, m_menuEntrySaveAttachedFiles);
3241 
3242 			m_milMain.CreateCopy(tsicEC, m_ctxEntryAutoTypeAdv, false, m_menuEntryPerformAutoType);
3243 			m_milMain.CreateLink(m_ctxEntryAutoTypeAdv, m_menuEntryAutoTypeAdv, false);
3244 
3245 			m_milMain.CreateCopy(tsicEC, m_ctxEntryEditQuick, false, m_menuEntryAdd);
3246 			m_milMain.CreateCopy(tsicEC, m_ctxEntryEditQuick, false, m_menuEntryEdit);
3247 			m_milMain.CreateLink(m_ctxEntryEditQuick, m_menuEntryEditQuick, false);
3248 
3249 			ToolStripItemCollection tsicECQuick = m_ctxEntryEditQuick.DropDownItems;
3250 			tsicECQuick.Insert(0, new ToolStripSeparator());
3251 			m_milMain.CreateCopy(tsicECQuick, null, false, m_menuEntryColor);
3252 			m_milMain.CreateCopy(tsicECQuick, null, false, m_menuEntryIcon);
3253 
3254 			m_milMain.CreateLink(m_ctxEntryTagAdd, m_menuEntryTagAdd, false);
3255 			m_milMain.CreateLink(m_ctxEntryTagRemove, m_menuEntryTagRemove, false);
3256 
3257 			ToolStripItemCollection tsicECTagAdd = m_ctxEntryTagAdd.DropDownItems;
3258 			m_milMain.CreateCopy(tsicECTagAdd, null, false, m_menuEntryTagNew);
3259 
3260 			tsicECQuick.Add(new ToolStripSeparator());
3261 			m_milMain.CreateCopy(tsicECQuick, null, true, m_menuEntryExpiresNow);
3262 			m_milMain.CreateCopy(tsicECQuick, null, true, m_menuEntryExpiresNever);
3263 
3264 			m_milMain.CreateCopy(tsicEC, m_ctxEntryEditQuick, true, m_menuEntryDelete);
3265 			m_milMain.CreateCopy(tsicEC, m_ctxEntryEditQuick, true, m_menuEntryDuplicate);
3266 
3267 			ToolStripSeparator ts = new ToolStripSeparator();
3268 			tsicEC.Insert(tsicEC.IndexOf(m_ctxEntryRearrange), ts);
3269 			m_milMain.CreateCopy(tsicEC, ts, false, m_menuEntrySelectAll);
3270 
3271 			m_milMain.CreateLink(m_ctxEntryRearrange, m_menuEntryRearrange, false);
3272 			m_milMain.CreateLink(m_ctxEntryMoveToGroup, m_menuEntryMoveToGroup, false);
3273 
3274 			ToolStripItemCollection tsicECMove = m_ctxEntryRearrange.DropDownItems;
3275 			tsicECMove.Insert(0, new ToolStripSeparator());
3276 			m_milMain.CreateCopy(tsicECMove, null, false, m_menuEntryMoveToBottom);
3277 			m_milMain.CreateCopy(tsicECMove, null, false, m_menuEntryMoveOneDown);
3278 			m_milMain.CreateCopy(tsicECMove, null, false, m_menuEntryMoveOneUp);
3279 			m_milMain.CreateCopy(tsicECMove, null, false, m_menuEntryMoveToTop);
3280 			m_milMain.CreateCopy(tsicECMove, null, true, m_menuEntryMoveToPreviousParent);
3281 
3282 			m_ctxPwList.ResumeLayout(true);
3283 			m_ctxGroupList.ResumeLayout(true);
3284 		}
3285 
CopyMenuItemText(ToolStripMenuItem tsmiTarget, ToolStripMenuItem tsmiCopyFrom, string strTextOpt)3286 		private static void CopyMenuItemText(ToolStripMenuItem tsmiTarget,
3287 			ToolStripMenuItem tsmiCopyFrom, string strTextOpt)
3288 		{
3289 			tsmiTarget.Text = (strTextOpt ?? tsmiCopyFrom.Text);
3290 
3291 			string strSh = tsmiCopyFrom.ShortcutKeyDisplayString;
3292 			if(!string.IsNullOrEmpty(strSh))
3293 				tsmiTarget.ShortcutKeyDisplayString = strSh;
3294 		}
3295 
3296 		// Public for plugins
SaveDatabaseAs(PwDatabase pdToSave, IOConnectionInfo iocTo, bool bOnline, object sender, bool bCopy)3297 		public void SaveDatabaseAs(PwDatabase pdToSave, IOConnectionInfo iocTo,
3298 			bool bOnline, object sender, bool bCopy)
3299 		{
3300 			PwDatabase pd = (pdToSave ?? m_docMgr.ActiveDatabase);
3301 
3302 			if(!pd.IsOpen) return;
3303 			if(!AppPolicy.Try(AppPolicyId.SaveFile)) return;
3304 
3305 			Guid eventGuid = Guid.NewGuid();
3306 			if(this.FileSavingPre != null)
3307 			{
3308 				FileSavingEventArgs args = new FileSavingEventArgs(true, bCopy, pd, eventGuid);
3309 				this.FileSavingPre(sender, args);
3310 				if(args.Cancel) return;
3311 			}
3312 			if(this.FileSaving != null)
3313 			{
3314 				FileSavingEventArgs args = new FileSavingEventArgs(true, bCopy, pd, eventGuid);
3315 				this.FileSaving(sender, args);
3316 				if(args.Cancel) return;
3317 			}
3318 
3319 			DialogResult dr;
3320 			IOConnectionInfo ioc = iocTo;
3321 
3322 			if((ioc != null) && (ioc.Path.Length > 0))
3323 			{
3324 				dr = DialogResult.OK; // Caller (plugin) specified target file
3325 			}
3326 			else if(bOnline)
3327 			{
3328 				IOConnectionForm iocf = new IOConnectionForm();
3329 				iocf.InitEx(true, pd.IOConnectionInfo, true, true);
3330 
3331 				dr = iocf.ShowDialog();
3332 				ioc = iocf.IOConnectionInfo;
3333 				UIUtil.DestroyForm(iocf);
3334 			}
3335 			else
3336 			{
3337 				SaveFileDialogEx sfdDb = UIUtil.CreateSaveFileDialog(KPRes.SaveDatabase,
3338 					UrlUtil.GetFileName(pd.IOConnectionInfo.Path),
3339 					UIUtil.CreateFileTypeFilter(AppDefs.FileExtension.FileExt,
3340 					KPRes.KdbxFiles, true), 1, AppDefs.FileExtension.FileExt,
3341 					AppDefs.FileDialogContext.Database);
3342 
3343 				GlobalWindowManager.AddDialog(sfdDb.FileDialog);
3344 				dr = sfdDb.ShowDialog();
3345 				GlobalWindowManager.RemoveDialog(sfdDb.FileDialog);
3346 
3347 				if(dr == DialogResult.OK)
3348 					ioc = IOConnectionInfo.FromPath(sfdDb.FileName);
3349 			}
3350 
3351 			if(dr == DialogResult.OK)
3352 			{
3353 				EcasPropertyDictionary dProps = new EcasPropertyDictionary();
3354 				dProps.Set(EcasProperty.IOConnectionInfo, ioc);
3355 				dProps.Set(EcasProperty.Database, pd);
3356 				Program.TriggerSystem.RaiseEvent(EcasEventIDs.SavingDatabaseFile,
3357 					dProps);
3358 
3359 				UIBlockInteraction(true);
3360 
3361 				ShutdownBlocker sdb = new ShutdownBlocker(this.Handle, KPRes.SavingDatabase);
3362 				ShowWarningsLogger swLogger = CreateShowWarningsLogger();
3363 				swLogger.StartLogging(KPRes.SavingDatabase, true);
3364 				m_sCancellable.Push(swLogger);
3365 
3366 				bool bSuccess = true;
3367 				try
3368 				{
3369 					PreSavingEx(pd, ioc);
3370 					pd.SaveAs(ioc, !bCopy, swLogger);
3371 					PostSavingEx(!bCopy, pd, ioc, swLogger);
3372 				}
3373 				catch(Exception exSaveAs)
3374 				{
3375 					MessageService.ShowSaveWarning(ioc, exSaveAs, true);
3376 					bSuccess = false;
3377 				}
3378 
3379 				m_sCancellable.Pop();
3380 				swLogger.EndLogging();
3381 				sdb.Dispose();
3382 
3383 				// Immediately after the UIBlockInteraction call the form might
3384 				// be closed and UpdateUIState might crash, if the order of the
3385 				// two methods is swapped; so first update state, then unblock
3386 				UpdateUIState(false);
3387 				UIBlockInteraction(false);
3388 
3389 				if(this.FileSaved != null)
3390 				{
3391 					FileSavedEventArgs args = new FileSavedEventArgs(bSuccess, pd, eventGuid);
3392 					this.FileSaved(sender, args);
3393 				}
3394 				if(bSuccess)
3395 					Program.TriggerSystem.RaiseEvent(EcasEventIDs.SavedDatabaseFile,
3396 						dProps);
3397 			}
3398 		}
3399 
PreSavingEx(PwDatabase pd, IOConnectionInfo ioc)3400 		private void PreSavingEx(PwDatabase pd, IOConnectionInfo ioc)
3401 		{
3402 			PerformSelfTest();
3403 
3404 			FixDuplicateUuids(pd, ioc);
3405 
3406 			pd.UseFileTransactions = Program.Config.Application.UseTransactedFileWrites;
3407 			pd.UseFileLocks = Program.Config.Application.UseFileLocks;
3408 
3409 			// AceColumn col = Program.Config.MainWindow.FindColumn(AceColumnType.Title);
3410 			// if((col != null) && !col.HideWithAsterisks)
3411 			//	pd.MemoryProtection.ProtectTitle = false;
3412 			// col = Program.Config.MainWindow.FindColumn(AceColumnType.UserName);
3413 			// if((col != null) && !col.HideWithAsterisks)
3414 			//	pd.MemoryProtection.ProtectUserName = false;
3415 			// col = Program.Config.MainWindow.FindColumn(AceColumnType.Url);
3416 			// if((col != null) && !col.HideWithAsterisks)
3417 			//	pd.MemoryProtection.ProtectUrl = false;
3418 			// col = Program.Config.MainWindow.FindColumn(AceColumnType.Notes);
3419 			// if((col != null) && !col.HideWithAsterisks)
3420 			//	pd.MemoryProtection.ProtectNotes = false;
3421 
3422 			if(pd == m_docMgr.ActiveDatabase) SaveWindowState();
3423 		}
3424 
PostSavingEx(bool bPrimary, PwDatabase pwDatabase, IOConnectionInfo ioc, IStatusLogger sl)3425 		private void PostSavingEx(bool bPrimary, PwDatabase pwDatabase,
3426 			IOConnectionInfo ioc, IStatusLogger sl)
3427 		{
3428 			if(ioc == null) { Debug.Assert(false); return; }
3429 
3430 			byte[] pbIO = null;
3431 			if(Program.Config.Application.VerifyWrittenFileAfterSaving)
3432 			{
3433 				pbIO = WinUtil.HashFile(ioc);
3434 				Debug.Assert((pbIO != null) && (pwDatabase.HashOfLastIO != null));
3435 				if(!MemUtil.ArraysEqual(pbIO, pwDatabase.HashOfLastIO))
3436 					MessageService.ShowWarning(ioc.GetDisplayName(),
3437 						KPRes.FileVerifyHashFail, KPRes.FileVerifyHashFailRec);
3438 			}
3439 
3440 			if(bPrimary)
3441 			{
3442 #if DEBUG
3443 				Debug.Assert(MemUtil.ArraysEqual(pbIO, pwDatabase.HashOfFileOnDisk));
3444 
3445 				try
3446 				{
3447 					PwDatabase pwCheck = new PwDatabase();
3448 					pwCheck.Open(ioc.CloneDeep(), pwDatabase.MasterKey, null);
3449 
3450 					Debug.Assert(MemUtil.ArraysEqual(pwDatabase.HashOfLastIO,
3451 						pwCheck.HashOfLastIO));
3452 
3453 					uint uGroups1, uGroups2, uEntries1, uEntries2;
3454 					pwDatabase.RootGroup.GetCounts(true, out uGroups1, out uEntries1);
3455 					pwCheck.RootGroup.GetCounts(true, out uGroups2, out uEntries2);
3456 					Debug.Assert((uGroups1 == uGroups2) && (uEntries1 == uEntries2));
3457 				}
3458 				catch(Exception exVerify) { Debug.Assert(false, exVerify.Message); }
3459 #endif
3460 
3461 				m_mruList.AddItem(ioc.GetDisplayName(), ioc.CloneDeep());
3462 
3463 				// SetLastUsedFile(ioc);
3464 
3465 				// if(Program.Config.Application.CreateBackupFileAfterSaving && bHashValid)
3466 				// {
3467 				//	try { pwDatabase.CreateBackupFile(sl); }
3468 				//	catch(Exception exBackup)
3469 				//	{
3470 				//		MessageService.ShowWarning(KPRes.FileBackupFailed, exBackup);
3471 				//	}
3472 				// }
3473 
3474 				// ulong uTotalBinSize = 0;
3475 				// EntryHandler ehCnt = delegate(PwEntry pe)
3476 				// {
3477 				//	foreach(KeyValuePair<string, ProtectedBinary> kvpCnt in pe.Binaries)
3478 				//	{
3479 				//		uTotalBinSize += kvpCnt.Value.Length;
3480 				//	}
3481 				//
3482 				//	return true;
3483 				// };
3484 				// pwDatabase.RootGroup.TraverseTree(TraversalMethod.PreOrder, null, ehCnt);
3485 			}
3486 
3487 			RememberKeySources(pwDatabase);
3488 			WinUtil.FlushStorageBuffers(ioc.Path, true);
3489 		}
3490 
UIFileSave(bool bForceSave)3491 		public bool UIFileSave(bool bForceSave)
3492 		{
3493 			Control cFocus = UIUtil.GetActiveControl(this);
3494 
3495 			PwDatabase pd = m_docMgr.ActiveDatabase;
3496 			pd.Modified = true;
3497 
3498 			if(bForceSave) ++m_uForceSave;
3499 			OnFileSave(null, EventArgs.Empty);
3500 			if(bForceSave) --m_uForceSave;
3501 
3502 			if(cFocus != null) ResetDefaultFocus(cFocus);
3503 			return !pd.Modified;
3504 		}
3505 
ResetDefaultFocus(Control cExplicit)3506 		public void ResetDefaultFocus(Control cExplicit)
3507 		{
3508 			Control c = cExplicit;
3509 
3510 			if(c == null)
3511 			{
3512 				// QuickFind must be the first choice (see e.g.
3513 				// the option FocusQuickFindOnUntray)
3514 				if(m_tbQuickFind.Visible && m_tbQuickFind.Enabled)
3515 					c = m_tbQuickFind.Control;
3516 				else if(m_lvEntries.Visible && m_lvEntries.Enabled)
3517 					c = m_lvEntries;
3518 				else if(m_tvGroups.Visible && m_tvGroups.Enabled)
3519 					c = m_tvGroups;
3520 				else if(m_richEntryView.Visible && m_richEntryView.Enabled)
3521 					c = m_richEntryView;
3522 				else c = m_lvEntries;
3523 			}
3524 
3525 			if(this.FocusChanging != null)
3526 			{
3527 				FocusEventArgs ea = new FocusEventArgs(cExplicit, c);
3528 				this.FocusChanging(null, ea);
3529 				if(ea.Cancel) return;
3530 			}
3531 
3532 			UIUtil.SetFocus(c, this);
3533 		}
3534 
PrepareLock()3535 		private static bool PrepareLock()
3536 		{
3537 			if(GlobalWindowManager.WindowCount == 0) return true;
3538 
3539 			if(GlobalWindowManager.CanCloseAllWindows)
3540 			{
3541 				GlobalWindowManager.CloseAllWindows();
3542 				return true;
3543 			}
3544 
3545 			return false;
3546 		}
3547 
UpdateImageLists(bool bForce)3548 		private void UpdateImageLists(bool bForce)
3549 		{
3550 			if(!bForce)
3551 			{
3552 				if(!m_docMgr.ActiveDatabase.UINeedsIconUpdate) return;
3553 				m_docMgr.ActiveDatabase.UINeedsIconUpdate = false;
3554 			}
3555 
3556 			int cx = DpiUtil.ScaleIntX(16);
3557 			int cy = DpiUtil.ScaleIntY(16);
3558 
3559 			// // foreach(Image img in m_ilClientIcons.Images) imgList.Images.Add(img);
3560 			// List<Image> lStdImages = new List<Image>();
3561 			// foreach(Image imgStd in m_ilClientIcons.Images)
3562 			// {
3563 			//	// Plugins may supply DPI-scaled images by changing m_ilClientIcons
3564 			//	bool bStd = (imgStd.Height == 16);
3565 			//	lStdImages.Add(bStd ? DpiUtil.ScaleImage(imgStd, false) : imgStd);
3566 			// }
3567 
3568 			if(m_lStdClientImages == null)
3569 			{
3570 				ImageArchive arStd = new ImageArchive();
3571 				arStd.Load(DpiUtil.ScalingRequired ?
3572 					Properties.Resources.Images_Client_HighRes :
3573 					Properties.Resources.Images_Client_16);
3574 
3575 				m_lStdClientImages = arStd.GetImages(cx, cy, true);
3576 			}
3577 
3578 			// ImageList imgListCustom = UIUtil.BuildImageList(
3579 			//	m_docMgr.ActiveDatabase.CustomIcons, cx, cy);
3580 			// foreach(Image imgCustom in imgListCustom.Images)
3581 			//	imgList.Images.Add(imgCustom); // Breaks alpha partially
3582 			List<Image> lCustom = UIUtil.BuildImageListEx(
3583 				m_docMgr.ActiveDatabase.CustomIcons, cx, cy);
3584 
3585 			List<Image> lAll = new List<Image>(m_lStdClientImages);
3586 			lAll.AddRange(lCustom);
3587 
3588 			ImageList imgList = new ImageList();
3589 			imgList.ImageSize = new Size(cx, cy);
3590 			imgList.ColorDepth = ColorDepth.Depth32Bit;
3591 
3592 			imgList.Images.AddRange(lAll.ToArray());
3593 			Debug.Assert(imgList.Images.Count == ((int)PwIcon.Count + lCustom.Count));
3594 
3595 			m_ilCurrentIcons = imgList;
3596 
3597 			if(UIUtil.VistaStyleListsSupported)
3598 			{
3599 				m_tvGroups.ImageList = imgList;
3600 				m_lvEntries.SmallImageList = imgList;
3601 			}
3602 			else
3603 			{
3604 				ImageList imgSafe = UIUtil.ConvertImageList24(lAll,
3605 					cx, cy, AppDefs.ColorControlNormal);
3606 				m_tvGroups.ImageList = imgSafe; // TreeView doesn't fully support alpha on < Vista
3607 				m_lvEntries.SmallImageList = ((WinUtil.IsAtLeastWindowsVista ||
3608 					WinUtil.IsWindowsXP) ? imgList : imgSafe);
3609 			}
3610 		}
3611 
MoveSelectedGroup(int iMove)3612 		private void MoveSelectedGroup(int iMove)
3613 		{
3614 			PwGroup pgMove = GetSelectedGroup();
3615 			if(pgMove == null) { Debug.Assert(false); return; }
3616 
3617 			PwGroup pgParent = pgMove.ParentGroup;
3618 			if(pgParent == null) return;
3619 
3620 			PwGroup[] pgAffected = new PwGroup[] { pgMove };
3621 
3622 			if(iMove == -2)
3623 				pgParent.Groups.MoveTopBottom(pgAffected, true);
3624 			else if(iMove == -1)
3625 				pgParent.Groups.MoveOne(pgMove, true);
3626 			else if(iMove == 1)
3627 				pgParent.Groups.MoveOne(pgMove, false);
3628 			else if(iMove == 2)
3629 				pgParent.Groups.MoveTopBottom(pgAffected, false);
3630 			else { Debug.Assert(false); return; }
3631 
3632 			pgMove.LocationChanged = DateTime.UtcNow;
3633 			UpdateUI(false, null, true, null, true, null, true);
3634 		}
3635 
OnEntryBinaryOpen(object sender, DynamicMenuEventArgs e)3636 		private void OnEntryBinaryOpen(object sender, DynamicMenuEventArgs e)
3637 		{
3638 			if(e == null) { Debug.Assert(false); return; }
3639 
3640 			EntryBinaryDataContext ctx = (e.Tag as EntryBinaryDataContext);
3641 			if(ctx == null) { Debug.Assert(false); return; }
3642 
3643 			PwEntry pe = ctx.Entry;
3644 			if(pe == null) { Debug.Assert(false); return; }
3645 			Debug.Assert(object.ReferenceEquals(pe, GetSelectedEntry(false)));
3646 
3647 			if(string.IsNullOrEmpty(ctx.Name)) { Debug.Assert(false); return; }
3648 			ProtectedBinary pb = pe.Binaries.Get(ctx.Name);
3649 			if(pb == null) { Debug.Assert(false); return; }
3650 
3651 			ProtectedBinary pbMod = BinaryDataUtil.Open(ctx.Name, pb, ctx.Options);
3652 			if(pbMod != null)
3653 			{
3654 				PwDatabase pd = m_docMgr.FindContainerOf(pe);
3655 				Debug.Assert(object.ReferenceEquals(pd, this.ActiveDatabase));
3656 				pe.CreateBackup(pd);
3657 
3658 				pe.Binaries.Set(ctx.Name, pbMod);
3659 				pe.Touch(true, false);
3660 
3661 				RefreshEntriesList();
3662 				UpdateUIState(true);
3663 			}
3664 		}
3665 
SaveWindowState()3666 		private void SaveWindowState()
3667 		{
3668 			PwDatabase pd = m_docMgr.ActiveDatabase;
3669 
3670 			PwGroup pgSelected = GetSelectedGroup();
3671 			if(pgSelected != null)
3672 				pd.LastSelectedGroup = new PwUuid(pgSelected.Uuid.UuidBytes);
3673 
3674 			TreeNode tnTop = m_tvGroups.TopNode;
3675 			if(tnTop != null)
3676 			{
3677 				PwGroup pgTop = (tnTop.Tag as PwGroup);
3678 				if(pgTop != null)
3679 					pd.LastTopVisibleGroup = new PwUuid(pgTop.Uuid.UuidBytes);
3680 				else { Debug.Assert(false); }
3681 			}
3682 
3683 			PwEntry peTop = GetTopEntry();
3684 			if((peTop != null) && (pgSelected != null))
3685 				pgSelected.LastTopVisibleEntry = new PwUuid(peTop.Uuid.UuidBytes);
3686 		}
3687 
RestoreWindowState(PwDatabase pd)3688 		private void RestoreWindowState(PwDatabase pd)
3689 		{
3690 			PwGroup pgSelect = null;
3691 
3692 			if(!pd.LastSelectedGroup.Equals(PwUuid.Zero))
3693 			{
3694 				pgSelect = pd.RootGroup.FindGroup(pd.LastSelectedGroup, true);
3695 				UpdateGroupList(pgSelect);
3696 				UpdateEntryList(pgSelect, false);
3697 			}
3698 
3699 			SetTopVisibleGroup(pd.LastTopVisibleGroup);
3700 			if(pgSelect != null) SetTopVisibleEntry(pgSelect.LastTopVisibleEntry);
3701 		}
3702 
SetTopVisibleGroup(PwUuid uuidGroup)3703 		private void SetTopVisibleGroup(PwUuid uuidGroup)
3704 		{
3705 			TreeNode tnTop = GuiFindGroup(uuidGroup, null);
3706 			if(tnTop != null) m_tvGroups.TopNode = tnTop;
3707 		}
3708 
SetTopVisibleEntry(PwUuid uuidEntry)3709 		private void SetTopVisibleEntry(PwUuid uuidEntry)
3710 		{
3711 			ListViewItem lviTop = GuiFindEntry(uuidEntry);
3712 			if(lviTop != null)
3713 				UIUtil.SetTopVisibleItem(m_lvEntries, lviTop.Index, false);
3714 		}
3715 
CloseDocument(PwDocument dsToClose, bool bLocking, bool bExiting, bool bEcas, bool bUpdateUI)3716 		internal void CloseDocument(PwDocument dsToClose, bool bLocking,
3717 			bool bExiting, bool bEcas, bool bUpdateUI)
3718 		{
3719 			PwDocument ds = (dsToClose ?? m_docMgr.ActiveDocument);
3720 			PwDatabase pd = ds.Database;
3721 			bool bIsActive = (ds == m_docMgr.ActiveDocument);
3722 
3723 			FileEventFlags f = FileEventFlags.None;
3724 			if(bLocking) f |= FileEventFlags.Locking;
3725 			if(bExiting) f |= FileEventFlags.Exiting;
3726 			if(bEcas) f |= FileEventFlags.Ecas;
3727 
3728 			Program.TriggerSystem.RaiseEvent(EcasEventIDs.ClosingDatabaseFilePre,
3729 				EcasProperty.Database, pd);
3730 			if(this.FileClosingPre != null)
3731 			{
3732 				FileClosingEventArgs fcea = new FileClosingEventArgs(pd, f);
3733 				this.FileClosingPre(null, fcea);
3734 				if(fcea.Cancel) return;
3735 			}
3736 
3737 			if(pd.Modified) // Implies pd.IsOpen
3738 			{
3739 				bool bInvokeSave = false;
3740 
3741 				// https://sourceforge.net/p/keepass/discussion/329220/thread/c3c823c6/
3742 				bool bCanAutoSave = AppPolicy.Current.SaveFile;
3743 
3744 				if(Program.Config.Application.FileClosing.AutoSave && bCanAutoSave)
3745 					bInvokeSave = true;
3746 				else
3747 				{
3748 					FileSaveOrigin fso = FileSaveOrigin.Closing;
3749 					if(bLocking) fso = FileSaveOrigin.Locking;
3750 					if(bExiting) fso = FileSaveOrigin.Exiting;
3751 
3752 					DialogResult dr = FileDialogsEx.ShowFileSaveQuestion(
3753 						pd.IOConnectionInfo.GetDisplayName(), fso);
3754 
3755 					if(dr == DialogResult.Cancel) return;
3756 					else if(dr == DialogResult.Yes) bInvokeSave = true;
3757 					else if(dr == DialogResult.No) { } // Changes are lost
3758 				}
3759 
3760 				if(bInvokeSave)
3761 				{
3762 					SaveDatabase(pd, null);
3763 					if(pd.Modified) return;
3764 				}
3765 			}
3766 
3767 			Program.TriggerSystem.RaiseEvent(EcasEventIDs.ClosingDatabaseFilePost,
3768 				EcasProperty.Database, pd);
3769 			if(this.FileClosingPost != null)
3770 			{
3771 				FileClosingEventArgs fcea = new FileClosingEventArgs(pd, f);
3772 				this.FileClosingPost(null, fcea);
3773 				if(fcea.Cancel) return;
3774 			}
3775 
3776 			IOConnectionInfo ioClosing = pd.IOConnectionInfo.CloneDeep();
3777 
3778 			pd.Close();
3779 			if(!bLocking) m_docMgr.CloseDatabase(pd);
3780 
3781 			if(bIsActive)
3782 			{
3783 				if((Program.Config.UI.UIFlags & (ulong)AceUIFlags.NoQuickSearchClear) == 0)
3784 				{
3785 					++m_uBlockQuickFind;
3786 					m_tbQuickFind.Items.Clear();
3787 					m_tbQuickFind.Text = string.Empty;
3788 					--m_uBlockQuickFind;
3789 				}
3790 
3791 				if(!bLocking)
3792 				{
3793 					m_docMgr.ActiveDatabase.UINeedsIconUpdate = true;
3794 					ResetDefaultFocus(null);
3795 				}
3796 			}
3797 			if(bUpdateUI)
3798 				UpdateUI(true, null, true, null, true, null, false);
3799 
3800 			// NativeMethods.ClearIconicBitmaps(this.Handle);
3801 			Program.TempFilesPool.Clear(TempClearFlags.ContentTaggedFiles);
3802 
3803 			if(this.FileClosed != null)
3804 			{
3805 				FileClosedEventArgs fcea = new FileClosedEventArgs(ioClosing, f);
3806 				this.FileClosed(null, fcea);
3807 			}
3808 		}
3809 
3810 		// Public for plugins
LockAllDocuments()3811 		public void LockAllDocuments()
3812 		{
3813 			NotifyUserActivity();
3814 
3815 			if(Program.Config.Security.WorkspaceLocking.AlwaysExitInsteadOfLocking)
3816 			{
3817 				OnFileExit(null, EventArgs.Empty);
3818 				return;
3819 			}
3820 
3821 			if(UIIsInteractionBlocked()) { Debug.Assert(false); return; }
3822 			if(!PrepareLock()) return; // Tries to close windows
3823 
3824 			SaveWindowState();
3825 
3826 			List<PwDocument> lDocs = m_docMgr.GetDocuments(int.MaxValue);
3827 			foreach(PwDocument ds in lDocs)
3828 			{
3829 				PwDatabase pd = ds.Database;
3830 				if(!pd.IsOpen) continue; // Nothing to lock
3831 
3832 				IOConnectionInfo ioIoc = pd.IOConnectionInfo;
3833 				Debug.Assert(ioIoc != null);
3834 
3835 				CloseDocument(ds, true, false, false, false);
3836 				if(pd.IsOpen) continue;
3837 
3838 				ds.LockedIoc = ioIoc;
3839 			}
3840 
3841 			UpdateUI(true, null, true, null, true, null, false);
3842 
3843 			if(Program.Config.MainWindow.MinimizeAfterLocking &&
3844 				!IsAtLeastOneFileOpen())
3845 				UIUtil.SetWindowState(this, FormWindowState.Minimized);
3846 		}
3847 
SaveAllDocuments()3848 		private void SaveAllDocuments()
3849 		{
3850 			List<PwDocument> lDocs = m_docMgr.GetDocuments(int.MaxValue);
3851 			foreach(PwDocument ds in lDocs)
3852 			{
3853 				PwDatabase pd = ds.Database;
3854 				if(!IsFileLocked(ds) && pd.Modified)
3855 					SaveDatabase(pd, null);
3856 			}
3857 
3858 			UpdateUI(false, null, true, null, true, null, false);
3859 		}
3860 
3861 		// Does not update the UI (for performance when exiting)
CloseAllDocuments(bool bExiting)3862 		private bool CloseAllDocuments(bool bExiting)
3863 		{
3864 			if(UIIsInteractionBlocked()) { Debug.Assert(false); return false; }
3865 
3866 			while(true)
3867 			{
3868 				List<PwDocument> lDocs = m_docMgr.GetDocuments(int.MaxValue);
3869 				if(lDocs.Count <= 0) { Debug.Assert(false); break; }
3870 
3871 				PwDocument ds = lDocs[0];
3872 				if((lDocs.Count == 1) && !ds.Database.IsOpen) break;
3873 
3874 				CloseDocument(ds, false, bExiting, false, false);
3875 				if(ds.Database.IsOpen) return false;
3876 			}
3877 
3878 			return true;
3879 		}
3880 
RecreateUITabs()3881 		private void RecreateUITabs()
3882 		{
3883 			++m_uTabChangeBlocked;
3884 			m_tabMain.SuspendLayout();
3885 
3886 			m_tabMain.TabPages.Clear();
3887 
3888 			m_tabMain.ImageList = null;
3889 			if(m_ilTabImages != null)
3890 			{
3891 				m_ilTabImages.Dispose();
3892 				m_ilTabImages = null;
3893 			}
3894 			foreach(Image img in m_lTabImages) img.Dispose();
3895 			m_lTabImages.Clear();
3896 
3897 			bool bImgs = !MonoWorkarounds.IsRequired(891029);
3898 
3899 			List<TabPage> lPages = new List<TabPage>();
3900 			for(int i = 0; i < m_docMgr.Documents.Count; ++i)
3901 			{
3902 				PwDocument ds = m_docMgr.Documents[i];
3903 				if(ds == null) { Debug.Assert(false); continue; }
3904 
3905 				TabPage tb = new TabPage();
3906 				tb.Tag = ds;
3907 
3908 				string strName, strTip;
3909 				GetTabText(ds, out strName, out strTip);
3910 				tb.Text = strName;
3911 				tb.ToolTipText = strTip;
3912 
3913 				if(bImgs && (ds.Database != null) && ds.Database.IsOpen &&
3914 					!UIUtil.ColorsEqual(ds.Database.Color, Color.Empty))
3915 				{
3916 					Image img = UIUtil.CreateTabColorImage(AppIcons.RoundColor(
3917 						ds.Database.Color), m_tabMain);
3918 					if(img != null)
3919 					{
3920 						m_lTabImages.Add(img);
3921 						tb.ImageIndex = m_lTabImages.Count - 1;
3922 					}
3923 					else { Debug.Assert(false); }
3924 				}
3925 
3926 				lPages.Add(tb);
3927 			}
3928 
3929 			if(m_lTabImages.Count > 0)
3930 			{
3931 				int qSize = m_lTabImages[0].Height;
3932 				m_ilTabImages = UIUtil.BuildImageListUnscaled(m_lTabImages, qSize, qSize);
3933 				m_tabMain.ImageList = m_ilTabImages;
3934 			}
3935 
3936 			m_tabMain.TabPages.AddRange(lPages.ToArray());
3937 
3938 			// m_tabMain.SelectedTab.Font = m_fontBoldUI;
3939 
3940 			m_tabMain.ResumeLayout();
3941 			--m_uTabChangeBlocked;
3942 		}
3943 
SelectUITab()3944 		private void SelectUITab()
3945 		{
3946 			++m_uTabChangeBlocked;
3947 
3948 			PwDocument dsSelect = m_docMgr.ActiveDocument;
3949 			foreach(TabPage tb in m_tabMain.TabPages)
3950 			{
3951 				if((PwDocument)tb.Tag == dsSelect)
3952 				{
3953 					m_tabMain.SelectedTab = tb;
3954 					break;
3955 				}
3956 			}
3957 
3958 			--m_uTabChangeBlocked;
3959 		}
3960 
MakeDocumentActive(PwDocument ds)3961 		public void MakeDocumentActive(PwDocument ds)
3962 		{
3963 			if(ds == null) { Debug.Assert(false); return; }
3964 
3965 			ds.Database.UINeedsIconUpdate = true;
3966 
3967 			UpdateUI(false, ds, true, null, true, null, false);
3968 
3969 			RestoreWindowState(ds.Database);
3970 			UpdateUIState(false);
3971 		}
3972 
GetTabText(PwDocument ds, out string strName, out string strTip)3973 		private void GetTabText(PwDocument ds, out string strName, out string strTip)
3974 		{
3975 			if(ds == null) { Debug.Assert(false); strName = string.Empty; strTip = string.Empty; return; }
3976 
3977 			bool bLocked = IsFileLocked(ds);
3978 			PwDatabase pd = (bLocked ? null : ds.Database);
3979 			if((pd == null) || !pd.IsOpen) bLocked = true;
3980 			string strPath = (bLocked ? ds.LockedIoc.Path : pd.IOConnectionInfo.Path);
3981 
3982 			strName = strPath;
3983 			// if(!Program.Config.MainWindow.ShowFullPathOnTab)
3984 			strName = UrlUtil.GetFileName(strName);
3985 
3986 			strTip = strPath;
3987 
3988 			if(bLocked) strName += " [" + KPRes.Locked + "]";
3989 			else
3990 			{
3991 				// if(Program.Config.MainWindow.ShowDatabaseNameOnTab && (pd.Name.Length != 0))
3992 				//	strName += " (" + pd.Name + ")";
3993 
3994 				if(pd.Modified) strName += "*";
3995 
3996 				if(pd.Name.Length != 0)
3997 					strTip += "\r\n\r\n" + pd.Name;
3998 				if(pd.Description.Length != 0)
3999 					strTip += "\r\n\r\n" + pd.Description;
4000 			}
4001 
4002 			strTip = StrUtil.EncodeToolTipText(strTip);
4003 		}
4004 
UpdateUITabs()4005 		private void UpdateUITabs()
4006 		{
4007 			foreach(TabPage tp in m_tabMain.TabPages)
4008 			{
4009 				PwDocument ds = (tp.Tag as PwDocument);
4010 				if(ds == null) { Debug.Assert(false); continue; }
4011 
4012 				string strName, strTip;
4013 				GetTabText(ds, out strName, out strTip);
4014 				tp.Text = strName;
4015 				tp.ToolTipText = strTip;
4016 			}
4017 		}
4018 
UpdateUI(bool bRecreateTabBar, PwDocument dsSelect, bool bUpdateGroupList, PwGroup pgSelect, bool bUpdateEntryList, PwGroup pgEntrySource, bool bSetModified)4019 		public void UpdateUI(bool bRecreateTabBar, PwDocument dsSelect,
4020 			bool bUpdateGroupList, PwGroup pgSelect, bool bUpdateEntryList,
4021 			PwGroup pgEntrySource, bool bSetModified)
4022 		{
4023 			UpdateUI(bRecreateTabBar, dsSelect, bUpdateGroupList, pgSelect,
4024 				bUpdateEntryList, pgEntrySource, bSetModified, null);
4025 		}
4026 
UpdateUI(bool bRecreateTabBar, PwDocument dsSelect, bool bUpdateGroupList, PwGroup pgSelect, bool bUpdateEntryList, PwGroup pgEntrySource, bool bSetModified, Control cOptFocus)4027 		public void UpdateUI(bool bRecreateTabBar, PwDocument dsSelect,
4028 			bool bUpdateGroupList, PwGroup pgSelect, bool bUpdateEntryList,
4029 			PwGroup pgEntrySource, bool bSetModified, Control cOptFocus)
4030 		{
4031 			if(bRecreateTabBar) RecreateUITabs();
4032 
4033 			if(dsSelect != null) m_docMgr.ActiveDocument = dsSelect;
4034 			SelectUITab();
4035 
4036 			UpdateImageLists(false);
4037 
4038 			if(bUpdateGroupList) UpdateGroupList(pgSelect);
4039 
4040 			if(bUpdateEntryList) UpdateEntryList(pgEntrySource, false);
4041 			else { Debug.Assert(pgEntrySource == null); }
4042 
4043 			UpdateUIState(bSetModified, cOptFocus);
4044 		}
4045 
ShowSearchResults(PwGroup pgResults, SearchParameters sp, PwGroup pgRoot, bool bFocusEntryList)4046 		private void ShowSearchResults(PwGroup pgResults, SearchParameters sp,
4047 			PwGroup pgRoot, bool bFocusEntryList)
4048 		{
4049 			if(pgResults == null) { Debug.Assert(false); return; }
4050 
4051 			UpdateEntryList(pgResults, false);
4052 			SelectFirstEntryIfNoneSelected();
4053 
4054 			if(bFocusEntryList) ResetDefaultFocus(m_lvEntries);
4055 
4056 			UpdateUIState(false);
4057 
4058 			PwGroup pgSkipped = null;
4059 			if((sp != null) && sp.RespectEntrySearchingDisabled)
4060 				pgSkipped = pgRoot;
4061 			ShowSearchResultsStatusMessage(pgSkipped);
4062 		}
4063 
ShowSearchResultsStatusMessage(PwGroup pgSearchSkippedRoot)4064 		private void ShowSearchResultsStatusMessage(PwGroup pgSearchSkippedRoot)
4065 		{
4066 			const string strParam = @"{PARAM}";
4067 
4068 			StringBuilder sb = new StringBuilder();
4069 
4070 			int n = m_lvEntries.Items.Count;
4071 			if(n == 1) sb.Append(KPRes.SearchEntriesFound1);
4072 			else
4073 			{
4074 				string strFound = KPRes.SearchEntriesFound;
4075 				string str = StrUtil.ReplaceCaseInsensitive(strFound,
4076 					strParam, n.ToString());
4077 				if(str == strFound) { Debug.Assert(false); return; }
4078 				sb.Append(str);
4079 			}
4080 
4081 			if(pgSearchSkippedRoot != null)
4082 			{
4083 				List<PwGroup> lSkipped = pgSearchSkippedRoot.GetTopSearchSkippedGroups();
4084 
4085 				if(lSkipped.Count > 0)
4086 				{
4087 					sb.Append(", ");
4088 
4089 					if(lSkipped.Count == 1) sb.Append(KPRes.GroupsSkipped1);
4090 					else
4091 					{
4092 						string strSkipped = KPRes.GroupsSkipped;
4093 						string str = StrUtil.ReplaceCaseInsensitive(strSkipped,
4094 							strParam, lSkipped.Count.ToString());
4095 						if(str == strSkipped) { Debug.Assert(false); return; }
4096 						sb.Append(str);
4097 					}
4098 
4099 					sb.Append(" (");
4100 
4101 					int m = Math.Min(lSkipped.Count, 3);
4102 					for(int i = 0; i < m; ++i)
4103 					{
4104 						if(i > 0) sb.Append(", ");
4105 						sb.Append('\'');
4106 						sb.Append(StrUtil.EncodeMenuText(lSkipped[i].Name.Trim()));
4107 						sb.Append('\'');
4108 					}
4109 					if(m < lSkipped.Count) sb.Append(", ...");
4110 
4111 					sb.Append(')');
4112 				}
4113 			}
4114 
4115 			sb.Append('.');
4116 			SetStatusEx(sb.ToString());
4117 		}
4118 
MinimizeToTray(bool bMinimize)4119 		private void MinimizeToTray(bool bMinimize)
4120 		{
4121 			if(bMinimize) // Order of Visible and ShowInTaskbar is important
4122 			{
4123 				// if(MonoWorkarounds.IsRequired(649266)) this.ShowInTaskbar = false;
4124 				this.Visible = false;
4125 			}
4126 			else
4127 			{
4128 				this.Visible = true;
4129 				// if(MonoWorkarounds.IsRequired(649266)) this.ShowInTaskbar = true;
4130 			}
4131 
4132 			if(bMinimize)
4133 			{
4134 				if(Program.Config.Security.WorkspaceLocking.LockOnWindowMinimizeToTray &&
4135 					!IsFileLocked(null))
4136 					OnFileLock(null, EventArgs.Empty);
4137 			}
4138 			else // Restore
4139 			{
4140 				// EnsureVisibleForegroundWindow(false, false); // Don't!
4141 
4142 				if(this.WindowState == FormWindowState.Minimized)
4143 					UIUtil.SetWindowState(this, (Program.Config.MainWindow.Maximized ?
4144 						FormWindowState.Maximized : FormWindowState.Normal));
4145 				else if(IsFileLocked(null) && !UIIsAutoUnlockBlocked())
4146 					OnFileLock(null, EventArgs.Empty); // Unlock
4147 
4148 				if(Program.Config.MainWindow.FocusQuickFindOnUntray)
4149 					ResetDefaultFocus(null);
4150 			}
4151 
4152 			UpdateTrayIcon(false);
4153 		}
4154 
GetStartMinimized()4155 		private bool GetStartMinimized()
4156 		{
4157 			return (Program.Config.Application.Start.MinimizedAndLocked ||
4158 				(Program.CommandLineArgs[AppDefs.CommandLineOptions.Minimize] != null));
4159 		}
4160 
MinimizeToTrayAtStartIfEnabled(bool bFormLoading)4161 		private void MinimizeToTrayAtStartIfEnabled(bool bFormLoading)
4162 		{
4163 			if(GetStartMinimized())
4164 			{
4165 				if(bFormLoading)
4166 					UIUtil.SetWindowState(this, FormWindowState.Minimized);
4167 				else
4168 				{
4169 					// The following isn't required anymore, because the
4170 					// TaskbarButtonCreated message is handled
4171 
4172 					// Set the lock overlay icon again (the first time
4173 					// Windows ignores the call, maybe because the window
4174 					// wasn't fully constructed at that time yet)
4175 					// if(IsFileLocked(null))
4176 					//	TaskbarList.SetOverlayIcon(this,
4177 					//		Properties.Resources.LockOverlay, KPRes.Locked);
4178 				}
4179 
4180 				if(Program.Config.MainWindow.MinimizeToTray) MinimizeToTray(true);
4181 				else if(!bFormLoading)
4182 					UIUtil.SetWindowState(this, FormWindowState.Minimized);
4183 			}
4184 
4185 			// Remove taskbar item
4186 			if(Program.Config.MainWindow.MinimizeAfterOpeningDatabase &&
4187 				Program.Config.MainWindow.MinimizeToTray && !bFormLoading &&
4188 				IsAtLeastOneFileOpen())
4189 			{
4190 				MinimizeToTray(true);
4191 			}
4192 		}
4193 
SelectFirstEntryIfNoneSelected()4194 		private void SelectFirstEntryIfNoneSelected()
4195 		{
4196 			if((m_lvEntries.Items.Count > 0) &&
4197 				(m_lvEntries.SelectedIndices.Count == 0))
4198 				UIUtil.SetFocusedItem(m_lvEntries, m_lvEntries.Items[0], true);
4199 		}
4200 
SelectEntries(PwObjectList<PwEntry> lEntries, bool bDeselectOthers, bool bFocusFirst)4201 		public void SelectEntries(PwObjectList<PwEntry> lEntries,
4202 			bool bDeselectOthers, bool bFocusFirst)
4203 		{
4204 			if(lEntries == null) { Debug.Assert(false); return; }
4205 
4206 			++m_uBlockEntrySelectionEvent;
4207 			try
4208 			{
4209 				bool bDoFocus = bFocusFirst;
4210 				foreach(ListViewItem lvi in m_lvEntries.Items)
4211 				{
4212 					if(lvi == null) { Debug.Assert(false); continue; }
4213 
4214 					PwListItem pli = (lvi.Tag as PwListItem);
4215 					if(pli == null) { Debug.Assert(false); continue; }
4216 
4217 					PwEntry pe = pli.Entry;
4218 					if(pe == null) { Debug.Assert(false); continue; }
4219 
4220 					if(lEntries.IndexOf(pe) >= 0)
4221 					{
4222 						lvi.Selected = true;
4223 
4224 						if(bDoFocus)
4225 						{
4226 							UIUtil.SetFocusedItem(m_lvEntries, lvi, false);
4227 							bDoFocus = false;
4228 						}
4229 					}
4230 					else if(bDeselectOthers) lvi.Selected = false;
4231 				}
4232 			}
4233 			catch(Exception) { Debug.Assert(false); }
4234 			finally { --m_uBlockEntrySelectionEvent; }
4235 		}
4236 
GetCurrentEntries()4237 		internal PwGroup GetCurrentEntries()
4238 		{
4239 			PwGroup pg = new PwGroup(true, true);
4240 			pg.IsVirtual = true;
4241 
4242 			if(!m_lvEntries.ShowGroups)
4243 			{
4244 				foreach(ListViewItem lvi in m_lvEntries.Items)
4245 					pg.AddEntry(((PwListItem)lvi.Tag).Entry, false);
4246 			}
4247 			else // Groups
4248 			{
4249 				foreach(ListViewGroup lvg in m_lvEntries.Groups)
4250 				{
4251 					foreach(ListViewItem lvi in lvg.Items)
4252 						pg.AddEntry(((PwListItem)lvi.Tag).Entry, false);
4253 				}
4254 			}
4255 
4256 			return pg;
4257 		}
4258 
4259 		// Public for plugins
EnsureVisibleEntry(PwUuid uuid)4260 		public void EnsureVisibleEntry(PwUuid uuid)
4261 		{
4262 			ListViewItem lvi = GuiFindEntry(uuid);
4263 			if(lvi == null) { Debug.Assert(false); return; }
4264 
4265 			m_lvEntries.EnsureVisible(lvi.Index);
4266 		}
4267 
4268 		internal void EnsureVisibleSelected(bool? obLast)
4269 		{
4270 			// PwEntry pe = GetSelectedEntry(true, bLast);
4271 			// if(pe == null) return;
4272 			// EnsureVisibleEntry(pe.Uuid);
4273 
4274 			ListView.SelectedIndexCollection lvsic = m_lvEntries.SelectedIndices;
4275 			if(lvsic == null) { Debug.Assert(false); return; }
4276 			int n = lvsic.Count;
4277 			if(n == 0) return;
4278 
4279 			// Getting lvsic[n - 1] sends n messages
4280 			if(obLast.HasValue)
4281 				m_lvEntries.EnsureVisible(obLast.Value ? lvsic[n - 1] : lvsic[0]);
4282 			else
4283 			{
4284 				int iFirst = lvsic[0];
4285 				if(n != 1) m_lvEntries.EnsureVisible(lvsic[n - 1]);
4286 				m_lvEntries.EnsureVisible(iFirst);
4287 			}
4288 		}
4289 
RemoveEntriesFromList(List<PwEntry> lEntries, bool bLockUIUpdate)4290 		private void RemoveEntriesFromList(List<PwEntry> lEntries, bool bLockUIUpdate)
4291 		{
4292 			Debug.Assert(lEntries != null); if(lEntries == null) return;
4293 			if(lEntries.Count == 0) return;
4294 
4295 			RemoveEntriesFromList(lEntries.ToArray(), bLockUIUpdate);
4296 		}
4297 
RemoveEntriesFromList(PwEntry[] vEntries, bool bLockUIUpdate)4298 		private void RemoveEntriesFromList(PwEntry[] vEntries, bool bLockUIUpdate)
4299 		{
4300 			Debug.Assert(vEntries != null); if(vEntries == null) return;
4301 			if(vEntries.Length == 0) return;
4302 
4303 			if(MonoWorkarounds.IsRequired(1690) && (m_lvEntries.Items.Count != 0))
4304 				UIUtil.SetFocusedItem(m_lvEntries, m_lvEntries.Items[0], false);
4305 
4306 			++m_uBlockEntrySelectionEvent;
4307 			if(bLockUIUpdate) m_lvEntries.BeginUpdate();
4308 
4309 			lock(m_asyncListUpdate.ListEditSyncObject)
4310 			{
4311 				for(int i = m_lvEntries.Items.Count - 1; i >= 0; --i)
4312 				{
4313 					PwEntry pe = ((PwListItem)m_lvEntries.Items[i].Tag).Entry;
4314 					Debug.Assert(pe != null);
4315 
4316 					if(Array.IndexOf<PwEntry>(vEntries, pe) >= 0)
4317 						m_lvEntries.Items.RemoveAt(i);
4318 				}
4319 			}
4320 
4321 			// Refresh alternating item background colors
4322 			UIUtil.SetAlternatingBgColors(m_lvEntries, m_clrAlternateItemBgColor,
4323 				Program.Config.MainWindow.EntryListAlternatingBgColors);
4324 
4325 			if(bLockUIUpdate) m_lvEntries.EndUpdate();
4326 			--m_uBlockEntrySelectionEvent;
4327 		}
4328 
PreSaveValidate(PwDatabase pd)4329 		private bool PreSaveValidate(PwDatabase pd)
4330 		{
4331 			if(m_uForceSave > 0) return true;
4332 
4333 			byte[] pbOnDisk = WinUtil.HashFile(pd.IOConnectionInfo);
4334 
4335 			if((pbOnDisk != null) && (pd.HashOfFileOnDisk != null) &&
4336 				!MemUtil.ArraysEqual(pbOnDisk, pd.HashOfFileOnDisk))
4337 			{
4338 				DialogResult dr = DialogResult.Yes;
4339 				if(!Program.Config.Application.SaveForceSync)
4340 					dr = AskIfSynchronizeInstead(pd.IOConnectionInfo);
4341 
4342 				if(dr == DialogResult.Yes) // Synchronize
4343 				{
4344 					bool? b = ImportUtil.Synchronize(pd, this, pd.IOConnectionInfo,
4345 						true, this);
4346 					UpdateUI(false, null, true, null, true, null, false);
4347 					if(b.HasValue) SetStatusEx(b.Value ? KPRes.SyncSuccess : KPRes.SyncFailed);
4348 					return false;
4349 				}
4350 				else if(dr == DialogResult.Cancel) return false;
4351 				else { Debug.Assert(dr == DialogResult.No); }
4352 			}
4353 
4354 			return true;
4355 		}
4356 
AskIfSynchronizeInstead(IOConnectionInfo ioc)4357 		private static DialogResult AskIfSynchronizeInstead(IOConnectionInfo ioc)
4358 		{
4359 			VistaTaskDialog dlg = new VistaTaskDialog();
4360 
4361 			string strText = string.Empty;
4362 			if(ioc.GetDisplayName().Length > 0)
4363 				strText += ioc.GetDisplayName() + MessageService.NewParagraph;
4364 			strText += KPRes.FileChanged;
4365 
4366 			dlg.CommandLinks = true;
4367 			dlg.WindowTitle = PwDefs.ShortProductName;
4368 			dlg.Content = strText;
4369 			dlg.SetIcon(VtdCustomIcon.Question);
4370 
4371 			dlg.MainInstruction = KPRes.OverwriteExistingFileQuestion;
4372 			dlg.AddButton((int)DialogResult.Yes, KPRes.Synchronize, KPRes.FileChangedSync);
4373 			dlg.AddButton((int)DialogResult.No, KPRes.Overwrite, KPRes.FileChangedOverwrite);
4374 			dlg.AddButton((int)DialogResult.Cancel, KPRes.Cancel, KPRes.FileSaveQOpCancel);
4375 
4376 			DialogResult dr;
4377 			if(dlg.ShowDialog()) dr = (DialogResult)dlg.Result;
4378 			else
4379 			{
4380 				strText += MessageService.NewParagraph;
4381 				strText += @"[" + KPRes.Yes + @"]: " + KPRes.Synchronize + @". " +
4382 					KPRes.FileChangedSync + MessageService.NewParagraph;
4383 				strText += @"[" + KPRes.No + @"]: " + KPRes.Overwrite + @". " +
4384 					KPRes.FileChangedOverwrite + MessageService.NewParagraph;
4385 				strText += @"[" + KPRes.Cancel + @"]: " + KPRes.FileSaveQOpCancel;
4386 
4387 				dr = MessageService.Ask(strText, PwDefs.ShortProductName,
4388 					MessageBoxButtons.YesNoCancel);
4389 			}
4390 
4391 			return dr;
4392 		}
4393 
ActivateNextDocumentEx(int iDir)4394 		private void ActivateNextDocumentEx(int iDir)
4395 		{
4396 			int n = m_tabMain.TabPages.Count;
4397 			if(n > 1)
4398 				m_tabMain.SelectedIndex = ((m_tabMain.SelectedIndex +
4399 					iDir + n) % n);
4400 		}
4401 
ProcessCmdKey(ref Message msg, Keys keyData)4402 		protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
4403 		{
4404 			bool bDown;
4405 			if(!NativeMethods.GetKeyMessageState(ref msg, out bDown))
4406 				return base.ProcessCmdKey(ref msg, keyData);
4407 
4408 			if(keyData == Keys.Escape) // No modifiers
4409 			{
4410 				// Control c = UIUtil.GetActiveControl(this); // Returns wrong control
4411 				// bool bMainMenu = (c == m_menuMain);
4412 				bool bMainMenu = ((msg.HWnd == m_menuMain.Handle) &&
4413 					(UIUtil.GetSelectedItem(m_menuMain.Items) != null));
4414 
4415 				if(!bMainMenu)
4416 				{
4417 					if(bDown && IsCommandTypeInvokable(null, AppCommandType.Window))
4418 					{
4419 						AceEscAction a = Program.Config.MainWindow.EscAction;
4420 
4421 						if(a == AceEscAction.Lock)
4422 							LockAllDocuments();
4423 						else if(a == AceEscAction.Minimize)
4424 							this.WindowState = FormWindowState.Minimized;
4425 						else if(a == AceEscAction.MinimizeToTray)
4426 						{
4427 							if(!IsTrayed()) MinimizeToTray(true);
4428 						}
4429 						else if(a == AceEscAction.Exit)
4430 							OnFileExit(null, EventArgs.Empty);
4431 						else { Debug.Assert(a == AceEscAction.None); }
4432 					}
4433 
4434 					return true;
4435 				}
4436 			}
4437 
4438 			KeyEventArgs e = new KeyEventArgs(keyData);
4439 			if(HandleMainWindowKeyMessage(e, bDown)) return true;
4440 
4441 			return base.ProcessCmdKey(ref msg, keyData);
4442 		}
4443 
HandleMainWindowKeyMessage(KeyEventArgs e, bool bDown)4444 		private bool HandleMainWindowKeyMessage(KeyEventArgs e, bool bDown)
4445 		{
4446 			if(e == null) { Debug.Assert(false); return false; }
4447 
4448 			Keys kc = e.KeyCode;
4449 			bool bCtrl = e.Control, bAlt = e.Alt, bShift = e.Shift;
4450 			bool bHandled = true;
4451 
4452 			// Enforce that Alt is up (e.g. on Polish systems AltGr+E,
4453 			// i.e. Ctrl+Alt+E, is used to enter a character)
4454 			if(bCtrl && !bAlt)
4455 			{
4456 				if(kc == Keys.Tab)
4457 				{
4458 					if(bDown) ActivateNextDocumentEx(bShift ? -1 : 1);
4459 				}
4460 				// When changing Ctrl+E, also change the tooltip of the quick search box
4461 				else if(kc == Keys.E) // Standard key for quick searches
4462 					ResetDefaultFocus(m_tbQuickFind.Control); // Down and up (RichTextBox)
4463 				else if(kc == Keys.H)
4464 				{
4465 					if(bDown) ToggleFieldAsterisks(AceColumnType.Password);
4466 				}
4467 				else if(kc == Keys.J)
4468 				{
4469 					if(bDown) ToggleFieldAsterisks(AceColumnType.UserName);
4470 				}
4471 				else bHandled = false;
4472 			}
4473 			else if(!bCtrl && !bAlt && (kc == Keys.F3))
4474 			{
4475 				if(bDown) OnFindInDatabase(null, EventArgs.Empty);
4476 			}
4477 			else bHandled = false;
4478 
4479 			if(bHandled) UIUtil.SetHandled(e, true);
4480 			return bHandled;
4481 		}
4482 
HandleMoveKeyMessage(KeyEventArgs e, bool bDown, bool bEntry)4483 		private bool HandleMoveKeyMessage(KeyEventArgs e, bool bDown, bool bEntry)
4484 		{
4485 			// Mono does not raise key events while Alt is down
4486 			bool bMoveMod = e.Alt;
4487 			if(MonoWorkarounds.IsRequired(1245)) bMoveMod = (e.Control && e.Shift);
4488 			if(!bMoveMod) return false;
4489 
4490 			// Mono raises key events *after* changing the selection,
4491 			// thus we cannot use any navigation keys
4492 			Keys[] vMove = new Keys[] { Keys.Home, Keys.Up, Keys.Down, Keys.End };
4493 			if(MonoWorkarounds.IsRequired(1245))
4494 				vMove = new Keys[] { Keys.F5, Keys.F6, Keys.F7, Keys.F8 };
4495 			int m = Array.IndexOf<Keys>(vMove, e.KeyCode);
4496 			if(m < 0) return false;
4497 
4498 			if(bDown)
4499 			{
4500 				EventHandler[] vHandlers;
4501 				if(bEntry)
4502 					vHandlers = new EventHandler[] { OnEntryMoveToTop,
4503 						OnEntryMoveOneUp, OnEntryMoveOneDown, OnEntryMoveToBottom };
4504 				else
4505 					vHandlers = new EventHandler[] { OnGroupMoveToTop,
4506 						OnGroupMoveOneUp, OnGroupMoveOneDown, OnGroupMoveToBottom };
4507 
4508 				vHandlers[m].Invoke(null, e);
4509 			}
4510 
4511 			UIUtil.SetHandled(e, true);
4512 			return true;
4513 		}
4514 
UIIsInteractionBlocked()4515 		public bool UIIsInteractionBlocked()
4516 		{
4517 			return (m_uUIBlocked > 0);
4518 		}
4519 
UIBlockInteraction(bool bBlock)4520 		public void UIBlockInteraction(bool bBlock)
4521 		{
4522 			NotifyUserActivity();
4523 
4524 			if(bBlock) ++m_uUIBlocked;
4525 			else if(m_uUIBlocked > 0) --m_uUIBlocked;
4526 			else { Debug.Assert(false); }
4527 
4528 			bool bNotBlocked = !UIIsInteractionBlocked();
4529 			this.Enabled = bNotBlocked;
4530 
4531 			if(bNotBlocked)
4532 			{
4533 				try { if(!IsPrimaryControlActive()) ResetDefaultFocus(null); }
4534 				catch(Exception) { Debug.Assert(false); }
4535 			}
4536 			else // Blocked
4537 			{
4538 				// Redraw the UI, but only when the UI is blocked (otherwise
4539 				// the Application.DoEvents call could indirectly run
4540 				// triggers/automations now, which could result in problems)
4541 				UIUtil.DoEventsByTime(true);
4542 			}
4543 		}
4544 
UIIsAutoUnlockBlocked()4545 		public bool UIIsAutoUnlockBlocked()
4546 		{
4547 			return (m_uUnlockAutoBlocked > 0);
4548 		}
4549 
UIBlockAutoUnlock(bool bBlock)4550 		public void UIBlockAutoUnlock(bool bBlock)
4551 		{
4552 			if(bBlock) ++m_uUnlockAutoBlocked;
4553 			else if(m_uUnlockAutoBlocked > 0) --m_uUnlockAutoBlocked;
4554 			else { Debug.Assert(false); }
4555 		}
4556 
UIIsWindowStateAutoBlocked()4557 		public bool UIIsWindowStateAutoBlocked()
4558 		{
4559 			return (m_uWindowStateAutoBlocked > 0);
4560 		}
4561 
UIBlockWindowStateAuto(bool bBlock)4562 		public void UIBlockWindowStateAuto(bool bBlock)
4563 		{
4564 			if(bBlock) ++m_uWindowStateAutoBlocked;
4565 			else if(m_uWindowStateAutoBlocked > 0) --m_uWindowStateAutoBlocked;
4566 			else { Debug.Assert(false); }
4567 		}
4568 
EnsureRecycleBin(ref PwGroup pgRecycleBin, PwDatabase pdContext, ref bool bGroupListUpdateRequired)4569 		private static void EnsureRecycleBin(ref PwGroup pgRecycleBin,
4570 			PwDatabase pdContext, ref bool bGroupListUpdateRequired)
4571 		{
4572 			if(pdContext == null) { Debug.Assert(false); return; }
4573 
4574 			if(pgRecycleBin == pdContext.RootGroup)
4575 			{
4576 				Debug.Assert(false);
4577 				pgRecycleBin = null;
4578 			}
4579 
4580 			if(pgRecycleBin == null)
4581 			{
4582 				pgRecycleBin = new PwGroup(true, true, KPRes.RecycleBin,
4583 					PwIcon.TrashBin);
4584 				pgRecycleBin.EnableAutoType = false;
4585 				pgRecycleBin.EnableSearching = false;
4586 				pgRecycleBin.IsExpanded = !Program.Config.Defaults.RecycleBinCollapse;
4587 				pdContext.RootGroup.AddGroup(pgRecycleBin, true);
4588 
4589 				pdContext.RecycleBinUuid = pgRecycleBin.Uuid;
4590 
4591 				bGroupListUpdateRequired = true;
4592 			}
4593 			else { Debug.Assert(pgRecycleBin.Uuid.Equals(pdContext.RecycleBinUuid)); }
4594 		}
4595 
DeleteSelectedEntries()4596 		private void DeleteSelectedEntries()
4597 		{
4598 			PwEntry[] vSelected = GetSelectedEntries();
4599 			if((vSelected == null) || (vSelected.Length == 0)) return;
4600 
4601 			PwDatabase pd = m_docMgr.ActiveDatabase;
4602 			PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);
4603 			bool bShiftPressed = ((Control.ModifierKeys & Keys.Shift) != Keys.None);
4604 
4605 			bool bAtLeastOnePermanent = false;
4606 			if(!pd.RecycleBinEnabled) bAtLeastOnePermanent = true;
4607 			else if(bShiftPressed) bAtLeastOnePermanent = true;
4608 			else if(pgRecycleBin == null) { } // Not permanent
4609 			else
4610 			{
4611 				foreach(PwEntry peEnum in vSelected)
4612 				{
4613 					if((peEnum.ParentGroup == pgRecycleBin) ||
4614 						peEnum.ParentGroup.IsContainedIn(pgRecycleBin))
4615 					{
4616 						bAtLeastOnePermanent = true;
4617 						break;
4618 					}
4619 				}
4620 			}
4621 
4622 			bool bSingle = (vSelected.Length == 1);
4623 			string strSummary = EntryUtil.CreateSummaryList(null, vSelected);
4624 
4625 			if(bAtLeastOnePermanent)
4626 			{
4627 				VistaTaskDialog dlg = new VistaTaskDialog();
4628 				dlg.CommandLinks = false;
4629 				dlg.Content = strSummary;
4630 				dlg.MainInstruction = (bSingle ? KPRes.DeleteEntriesQuestionSingle :
4631 					KPRes.DeleteEntriesQuestion);
4632 				dlg.SetIcon(VtdCustomIcon.Question);
4633 				dlg.WindowTitle = PwDefs.ShortProductName;
4634 				dlg.AddButton((int)DialogResult.OK, KPRes.DeleteCmd, null);
4635 				dlg.AddButton((int)DialogResult.Cancel, KPRes.Cancel, null);
4636 
4637 				if(dlg.ShowDialog())
4638 				{
4639 					if(dlg.Result == (int)DialogResult.Cancel) return;
4640 				}
4641 				else
4642 				{
4643 					string strText = (bSingle ? KPRes.DeleteEntriesQuestionSingle :
4644 						KPRes.DeleteEntriesQuestion);
4645 					if(strSummary.Length > 0)
4646 						strText += MessageService.NewParagraph + strSummary;
4647 
4648 					if(!MessageService.AskYesNo(strText, (bSingle ?
4649 						KPRes.DeleteEntriesTitleSingle : KPRes.DeleteEntriesTitle)))
4650 						return;
4651 				}
4652 			}
4653 			else if(Program.Config.UI.ShowRecycleConfirmDialog)
4654 			{
4655 				VistaTaskDialog dlg = new VistaTaskDialog();
4656 				dlg.CommandLinks = false;
4657 				dlg.Content = strSummary;
4658 				dlg.MainInstruction = (bSingle ? KPRes.RecycleEntryConfirmSingle :
4659 					KPRes.RecycleEntryConfirm);
4660 				dlg.SetIcon(VtdCustomIcon.Question);
4661 				dlg.VerificationText = KPRes.DialogNoShowAgain;
4662 				dlg.WindowTitle = PwDefs.ShortProductName;
4663 				dlg.AddButton((int)DialogResult.OK, KPRes.YesCmd, null);
4664 				dlg.AddButton((int)DialogResult.Cancel, KPRes.NoCmd, null);
4665 
4666 				if(dlg.ShowDialog())
4667 				{
4668 					if(dlg.Result == (int)DialogResult.Cancel) return;
4669 
4670 					if(dlg.ResultVerificationChecked)
4671 						Program.Config.UI.ShowRecycleConfirmDialog = false;
4672 				}
4673 				else
4674 				{
4675 					string strText = (bSingle ? KPRes.RecycleEntryConfirmSingle :
4676 						KPRes.RecycleEntryConfirm);
4677 					if(strSummary.Length > 0)
4678 						strText += MessageService.NewParagraph + strSummary;
4679 
4680 					if(!MessageService.AskYesNo(strText, (bSingle ?
4681 						KPRes.DeleteEntriesTitleSingle : KPRes.DeleteEntriesTitle)))
4682 						return;
4683 				}
4684 			}
4685 
4686 			bool bUpdateGroupList = false;
4687 			DateTime dtNow = DateTime.UtcNow;
4688 			foreach(PwEntry pe in vSelected)
4689 			{
4690 				PwGroup pgParent = pe.ParentGroup;
4691 				if(pgParent == null) continue; // Can't remove
4692 
4693 				if(!pgParent.Entries.Remove(pe)) { Debug.Assert(false); continue; }
4694 
4695 				bool bPermanent = false;
4696 				if(!pd.RecycleBinEnabled) bPermanent = true;
4697 				else if(bShiftPressed) bPermanent = true;
4698 				else if(pgRecycleBin == null) { } // Recycle
4699 				else if(pgParent == pgRecycleBin) bPermanent = true;
4700 				else if(pgParent.IsContainedIn(pgRecycleBin)) bPermanent = true;
4701 
4702 				if(bPermanent)
4703 				{
4704 					PwDeletedObject pdo = new PwDeletedObject(pe.Uuid, dtNow);
4705 					pd.DeletedObjects.Add(pdo);
4706 				}
4707 				else // Recycle
4708 				{
4709 					EnsureRecycleBin(ref pgRecycleBin, pd, ref bUpdateGroupList);
4710 
4711 					pgRecycleBin.AddEntry(pe, true, true);
4712 					pe.PreviousParentGroup = pgParent.Uuid;
4713 					pe.Touch(false);
4714 				}
4715 			}
4716 
4717 			RemoveEntriesFromList(vSelected, true);
4718 			UpdateUI(false, null, bUpdateGroupList, null, false, null, true);
4719 		}
4720 
DeleteSelectedGroup()4721 		private void DeleteSelectedGroup()
4722 		{
4723 			PwGroup pg = GetSelectedGroup();
4724 			if(pg == null) { Debug.Assert(false); return; }
4725 
4726 			PwGroup pgParent = pg.ParentGroup;
4727 			if(pgParent == null) return; // Can't remove virtual or root group
4728 
4729 			PwDatabase pd = m_docMgr.ActiveDatabase;
4730 			PwGroup pgRecycleBin = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);
4731 			bool bShiftPressed = ((Control.ModifierKeys & Keys.Shift) != Keys.None);
4732 
4733 			bool bPermanent = false;
4734 			if(!pd.RecycleBinEnabled) bPermanent = true;
4735 			else if(bShiftPressed) bPermanent = true;
4736 			else if(pgRecycleBin == null) { }
4737 			else if(pg == pgRecycleBin) bPermanent = true;
4738 			else if(pg.IsContainedIn(pgRecycleBin)) bPermanent = true;
4739 			else if(pgRecycleBin.IsContainedIn(pg)) bPermanent = true;
4740 
4741 			string strContent = EntryUtil.CreateSummaryList(pg, false);
4742 			if(strContent.Length > 0)
4743 				strContent = KPRes.DeleteGroupInfo + MessageService.NewParagraph +
4744 					strContent;
4745 
4746 			if(bPermanent)
4747 			{
4748 				VistaTaskDialog dlg = new VistaTaskDialog();
4749 				dlg.CommandLinks = false;
4750 				dlg.Content = strContent;
4751 				dlg.MainInstruction = KPRes.DeleteGroupQuestion;
4752 				dlg.SetIcon(VtdCustomIcon.Question);
4753 				dlg.WindowTitle = PwDefs.ShortProductName;
4754 				dlg.AddButton((int)DialogResult.OK, KPRes.DeleteCmd, null);
4755 				dlg.AddButton((int)DialogResult.Cancel, KPRes.Cancel, null);
4756 
4757 				if(dlg.ShowDialog())
4758 				{
4759 					if(dlg.Result == (int)DialogResult.Cancel) return;
4760 				}
4761 				else
4762 				{
4763 					string strText = KPRes.DeleteGroupQuestion;
4764 					if(strContent.Length > 0)
4765 						strText += MessageService.NewParagraph + strContent;
4766 
4767 					if(!MessageService.AskYesNo(strText, KPRes.DeleteGroupTitle))
4768 						return;
4769 				}
4770 			}
4771 			else if(Program.Config.UI.ShowRecycleConfirmDialog)
4772 			{
4773 				VistaTaskDialog dlg = new VistaTaskDialog();
4774 				dlg.CommandLinks = false;
4775 				dlg.Content = strContent;
4776 				dlg.MainInstruction = KPRes.RecycleGroupConfirm;
4777 				dlg.SetIcon(VtdCustomIcon.Question);
4778 				dlg.VerificationText = KPRes.DialogNoShowAgain;
4779 				dlg.WindowTitle = PwDefs.ShortProductName;
4780 				dlg.AddButton((int)DialogResult.OK, KPRes.YesCmd, null);
4781 				dlg.AddButton((int)DialogResult.Cancel, KPRes.NoCmd, null);
4782 
4783 				if(dlg.ShowDialog())
4784 				{
4785 					if(dlg.Result == (int)DialogResult.Cancel) return;
4786 
4787 					if(dlg.ResultVerificationChecked)
4788 						Program.Config.UI.ShowRecycleConfirmDialog = false;
4789 				}
4790 				else
4791 				{
4792 					string strText = KPRes.RecycleGroupConfirm;
4793 					if(strContent.Length > 0)
4794 						strText += MessageService.NewParagraph + strContent;
4795 
4796 					if(!MessageService.AskYesNo(strText, KPRes.DeleteGroupTitle))
4797 						return;
4798 				}
4799 			}
4800 
4801 			if(!pgParent.Groups.Remove(pg)) { Debug.Assert(false); return; }
4802 
4803 			if(bPermanent)
4804 			{
4805 				pg.DeleteAllObjects(pd);
4806 
4807 				PwDeletedObject pdo = new PwDeletedObject(pg.Uuid, DateTime.UtcNow);
4808 				pd.DeletedObjects.Add(pdo);
4809 			}
4810 			else // Recycle
4811 			{
4812 				bool bDummy = false;
4813 				EnsureRecycleBin(ref pgRecycleBin, pd, ref bDummy);
4814 
4815 				try
4816 				{
4817 					pgRecycleBin.AddGroup(pg, true, true);
4818 					pg.PreviousParentGroup = pgParent.Uuid;
4819 				}
4820 				catch(Exception ex)
4821 				{
4822 					if(pgRecycleBin.Groups.IndexOf(pg) < 0)
4823 						pgParent.AddGroup(pg, true, true); // Undo removal
4824 					MessageService.ShowWarning(ex);
4825 				}
4826 
4827 				pg.Touch(false);
4828 			}
4829 
4830 			UpdateUI(false, null, true, pgParent, true, null, true);
4831 		}
4832 
EmptyRecycleBin()4833 		private void EmptyRecycleBin()
4834 		{
4835 			PwDatabase pd = m_docMgr.ActiveDatabase;
4836 			if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; }
4837 
4838 			PwGroup pg = pd.RootGroup.FindGroup(pd.RecycleBinUuid, true);
4839 			if(pg == null) { Debug.Assert(false); return; }
4840 
4841 			string strSummary = EntryUtil.CreateSummaryList(pg, false);
4842 
4843 			VistaTaskDialog dlg = new VistaTaskDialog();
4844 			dlg.CommandLinks = false;
4845 			dlg.Content = strSummary;
4846 			dlg.MainInstruction = KPRes.EmptyRecycleBinQuestion;
4847 			dlg.SetIcon(VtdCustomIcon.Question);
4848 			dlg.WindowTitle = PwDefs.ShortProductName;
4849 			dlg.AddButton((int)DialogResult.OK, KPRes.DeleteCmd, null);
4850 			dlg.AddButton((int)DialogResult.Cancel, KPRes.Cancel, null);
4851 
4852 			if(dlg.ShowDialog())
4853 			{
4854 				if(dlg.Result == (int)DialogResult.Cancel) return;
4855 			}
4856 			else
4857 			{
4858 				string strText = KPRes.EmptyRecycleBinQuestion;
4859 				if(strSummary.Length > 0)
4860 					strText += MessageService.NewParagraph + strSummary;
4861 
4862 				if(!MessageService.AskYesNo(strText))
4863 					return;
4864 			}
4865 
4866 			pg.DeleteAllObjects(pd);
4867 
4868 			UpdateUI(false, null, true, pg, true, null, true);
4869 		}
4870 
4871 		// private static string GetGroupSuffixText(PwGroup pg)
4872 		// {
4873 		// if(pg == null) { Debug.Assert(false); return string.Empty; }
4874 		// if(pg.Entries.UCount == 0) return string.Empty;
4875 		// if(!GroupOnlyContainsTans(pg, true)) return string.Empty;
4876 		// DateTime dtNow = DateTime.UtcNow;
4877 		// uint uValid = 0;
4878 		// foreach(PwEntry pe in pg.Entries)
4879 		// {
4880 		//	if(pe.Expires && (pe.ExpiryTime <= dtNow)) { }
4881 		//	else ++uValid;
4882 		// }
4883 		// return (" (" + uValid.ToString() + "/" + pg.Entries.UCount.ToString() + ")");
4884 		// }
4885 
AddCustomToolBarButton(string strID, string strName, string strDesc)4886 		public void AddCustomToolBarButton(string strID, string strName, string strDesc)
4887 		{
4888 			if(string.IsNullOrEmpty(strID)) { Debug.Assert(false); return; } // No throw
4889 			if(string.IsNullOrEmpty(strName)) { Debug.Assert(false); return; } // No throw
4890 
4891 			if(m_vCustomToolBarButtons.Count == 0)
4892 			{
4893 				m_tsSepCustomToolBar = new ToolStripSeparator();
4894 				m_toolMain.Items.Add(m_tsSepCustomToolBar);
4895 			}
4896 
4897 			ToolStripButton btn = new ToolStripButton(strName);
4898 			btn.Tag = strID;
4899 			btn.Click += OnCustomToolBarButtonClicked;
4900 			if(!string.IsNullOrEmpty(strDesc)) btn.ToolTipText = strDesc;
4901 
4902 			m_toolMain.Items.Add(btn);
4903 			m_vCustomToolBarButtons.Add(btn);
4904 		}
4905 
RemoveCustomToolBarButton(string strID)4906 		public void RemoveCustomToolBarButton(string strID)
4907 		{
4908 			if(string.IsNullOrEmpty(strID)) { Debug.Assert(false); return; } // No throw
4909 
4910 			foreach(ToolStripButton tb in m_vCustomToolBarButtons)
4911 			{
4912 				string str = (tb.Tag as string);
4913 				if(string.IsNullOrEmpty(str)) { Debug.Assert(false); continue; }
4914 
4915 				if(str.Equals(strID, StrUtil.CaseIgnoreCmp))
4916 				{
4917 					tb.Click -= OnCustomToolBarButtonClicked;
4918 					m_toolMain.Items.Remove(tb);
4919 					m_vCustomToolBarButtons.Remove(tb);
4920 					break;
4921 				}
4922 			}
4923 
4924 			if((m_vCustomToolBarButtons.Count == 0) && (m_tsSepCustomToolBar != null))
4925 			{
4926 				m_toolMain.Items.Remove(m_tsSepCustomToolBar);
4927 				m_tsSepCustomToolBar = null;
4928 			}
4929 		}
4930 
OnCustomToolBarButtonClicked(object sender, EventArgs e)4931 		private void OnCustomToolBarButtonClicked(object sender, EventArgs e)
4932 		{
4933 			ToolStripButton btn = (sender as ToolStripButton);
4934 			if(btn == null) { Debug.Assert(false); return; }
4935 
4936 			string strID = (btn.Tag as string);
4937 			if(string.IsNullOrEmpty(strID)) { Debug.Assert(false); return; }
4938 
4939 			Program.TriggerSystem.RaiseEvent(EcasEventIDs.CustomTbButtonClicked,
4940 				EcasProperty.CommandID, strID);
4941 		}
4942 
IocFromCommandLine()4943 		internal IOConnectionInfo IocFromCommandLine()
4944 		{
4945 			CommandLineArgs args = Program.CommandLineArgs;
4946 			IOConnectionInfo ioc = IOConnectionInfo.FromPath(args.FileName ??
4947 				string.Empty);
4948 
4949 			if(ioc.IsLocalFile()) // Expand relative path to absolute
4950 				ioc.Path = UrlUtil.MakeAbsolutePath(UrlUtil.EnsureTerminatingSeparator(
4951 					WinUtil.GetWorkingDirectory(), false) + "Sentinel", ioc.Path);
4952 
4953 			// Set the user name, which acts as a filter for the MRU items
4954 			string strUserName = args[AppDefs.CommandLineOptions.IoCredUserName];
4955 			if(strUserName != null) ioc.UserName = strUserName;
4956 
4957 			if(args[AppDefs.CommandLineOptions.IoCredFromRecent] != null)
4958 				ioc = CompleteConnectionInfoUsingMru(ioc);
4959 
4960 			// Override the password
4961 			string strPassword = args[AppDefs.CommandLineOptions.IoCredPassword];
4962 			if(strPassword != null) ioc.Password = strPassword;
4963 
4964 			string strComplete = args[AppDefs.CommandLineOptions.IoCredIsComplete];
4965 			if(strComplete != null) ioc.IsComplete = true;
4966 
4967 			return ioc;
4968 		}
4969 
RememberKeySources(PwDatabase pwDb)4970 		private static void RememberKeySources(PwDatabase pwDb)
4971 		{
4972 			if(pwDb == null) { Debug.Assert(false); return; }
4973 
4974 			Program.Config.Defaults.SetKeySources(pwDb.IOConnectionInfo,
4975 				pwDb.MasterKey);
4976 		}
4977 
SerializeMruList(bool bStore)4978 		private void SerializeMruList(bool bStore)
4979 		{
4980 			AceMru aceMru = Program.Config.Application.MostRecentlyUsed;
4981 
4982 			if(bStore)
4983 			{
4984 				if(!m_mruList.IsValid) { Debug.Assert(false); return; }
4985 
4986 				aceMru.MaxItemCount = m_mruList.MaxItemCount;
4987 
4988 				aceMru.Items.Clear();
4989 				// Count <= max is not guaranteed, therefore take minimum of both
4990 				uint uMax = Math.Min(m_mruList.MaxItemCount, m_mruList.ItemCount);
4991 				for(uint uMru = 0; uMru < uMax; ++uMru)
4992 				{
4993 					KeyValuePair<string, object> kvpMru = m_mruList.GetItem(uMru);
4994 					IOConnectionInfo ioMru = (kvpMru.Value as IOConnectionInfo);
4995 					if(ioMru != null) aceMru.Items.Add(ioMru);
4996 					else { Debug.Assert(false); }
4997 				}
4998 			}
4999 			else // Load
5000 			{
5001 				m_mruList.MaxItemCount = aceMru.MaxItemCount;
5002 
5003 				int nMax = Math.Min((int)m_mruList.MaxItemCount, aceMru.Items.Count);
5004 				for(int iMru = 0; iMru < nMax; ++iMru)
5005 				{
5006 					IOConnectionInfo ioMru = aceMru.Items[nMax - iMru - 1];
5007 					m_mruList.AddItem(ioMru.GetDisplayName(), ioMru.CloneDeep());
5008 				}
5009 			}
5010 		}
5011 
5012 		[Obsolete]
RedirectActivationPush(Form formTarget)5013 		public void RedirectActivationPush(Form formTarget)
5014 		{
5015 			// m_vRedirectActivation.Push(formTarget);
5016 		}
5017 
5018 		[Obsolete]
RedirectActivationPop()5019 		public void RedirectActivationPop()
5020 		{
5021 			// if(m_vRedirectActivation.Count == 0) { Debug.Assert(false); return; }
5022 			// m_vRedirectActivation.Pop();
5023 		}
5024 
ChangeMasterKey(PwDatabase pdOf)5025 		private bool ChangeMasterKey(PwDatabase pdOf)
5026 		{
5027 			PwDatabase pd = (pdOf ?? m_docMgr.ActiveDatabase);
5028 			if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return false; }
5029 
5030 			if(!AppPolicy.Try(AppPolicyId.ChangeMasterKey)) return false;
5031 			if(!AppPolicy.Current.ChangeMasterKeyNoKey)
5032 			{
5033 				if(!KeyUtil.ReAskKey(pd, true)) return false;
5034 			}
5035 
5036 			KeyCreationForm kcf = new KeyCreationForm();
5037 			kcf.InitEx(pd.IOConnectionInfo, false);
5038 
5039 			if(UIUtil.ShowDialogNotValue(kcf, DialogResult.OK)) return false;
5040 
5041 			pd.MasterKey = kcf.CompositeKey;
5042 			pd.MasterKeyChanged = DateTime.UtcNow;
5043 			// pd.MasterKeyChangeForceOnce = false;
5044 			pd.Modified = true;
5045 
5046 			UIUtil.DestroyForm(kcf);
5047 			UpdateUIState(false); // Show modified state in the UI
5048 
5049 			string str = KPRes.MasterKeyChanged + MessageService.NewParagraph +
5050 				KPRes.MasterKeyChangedSave + MessageService.NewParagraph +
5051 				KPRes.FileSaveQ;
5052 
5053 			VistaTaskDialog dlg = new VistaTaskDialog();
5054 			dlg.CommandLinks = false;
5055 			dlg.Content = str;
5056 			dlg.DefaultButtonID = (int)DialogResult.OK;
5057 			dlg.MainInstruction = KPRes.MasterKeyChangedShort;
5058 			dlg.WindowTitle = PwDefs.ShortProductName;
5059 
5060 			dlg.SetIcon(VtdCustomIcon.Question);
5061 			dlg.AddButton((int)DialogResult.OK, KPRes.Save, null);
5062 			dlg.AddButton((int)DialogResult.Cancel, KPRes.NotNow, null);
5063 
5064 			bool b;
5065 			if(dlg.ShowDialog(this)) b = (dlg.Result == (int)DialogResult.OK);
5066 			else b = MessageService.AskYesNo(str);
5067 
5068 			if(b)
5069 			{
5070 				SaveDatabase(pd, null);
5071 
5072 				// Show emergency sheet creation prompt only when the
5073 				// database was saved successfully, otherwise the sheet
5074 				// could contain incorrect information
5075 				if(!pd.Modified) EmergencySheet.AskCreate(pd);
5076 			}
5077 
5078 			return true;
5079 		}
5080 
UpdateColumnsEx(bool bGuiToInternal)5081 		private void UpdateColumnsEx(bool bGuiToInternal)
5082 		{
5083 			if(m_bBlockColumnUpdates) return;
5084 			m_bBlockColumnUpdates = true;
5085 
5086 			if(!bGuiToInternal) // Internal to GUI
5087 			{
5088 				m_asyncListUpdate.CancelPendingUpdatesAsync();
5089 
5090 				m_lvEntries.BeginUpdate();
5091 				lock(m_asyncListUpdate.ListEditSyncObject)
5092 				{
5093 					m_lvEntries.Items.Clear();
5094 				}
5095 				m_lvEntries.Columns.Clear();
5096 
5097 				if(Program.Config.MainWindow.EntryListColumns.Count == 0)
5098 				{
5099 					int nWidth = (m_lvEntries.ClientRectangle.Width -
5100 						UIUtil.GetVScrollBarWidth()) / 5;
5101 					EntryListAddColumn(AceColumnType.Title, nWidth, false);
5102 					EntryListAddColumn(AceColumnType.UserName, nWidth, false);
5103 					EntryListAddColumn(AceColumnType.Password, nWidth, true);
5104 					EntryListAddColumn(AceColumnType.Url, nWidth, false);
5105 					EntryListAddColumn(AceColumnType.Notes, nWidth, false);
5106 				}
5107 
5108 				int nDefaultWidth = m_lvEntries.ClientRectangle.Width /
5109 					Program.Config.MainWindow.EntryListColumns.Count;
5110 				foreach(AceColumn col in Program.Config.MainWindow.EntryListColumns)
5111 				{
5112 					ColumnHeader ch = m_lvEntries.Columns.Add(col.GetDisplayName(),
5113 						col.SafeGetWidth(nDefaultWidth));
5114 
5115 					HorizontalAlignment ha;
5116 					if(col.Type == AceColumnType.PluginExt)
5117 						ha = Program.ColumnProviderPool.GetTextAlign(col.CustomName);
5118 					else ha = AceColumn.GetTextAlign(col.Type);
5119 					if(ha != HorizontalAlignment.Left) ch.TextAlign = ha;
5120 				}
5121 
5122 				int[] vDisplayIndices = StrUtil.DeserializeIntArray(
5123 					Program.Config.MainWindow.EntryListColumnDisplayOrder);
5124 				UIUtil.SetDisplayIndices(m_lvEntries, vDisplayIndices);
5125 
5126 				m_lvEntries.EndUpdate();
5127 				UpdateColumnSortingIcons();
5128 			}
5129 			else // GUI to internal
5130 			{
5131 				List<AceColumn> l = Program.Config.MainWindow.EntryListColumns;
5132 				if(l.Count == m_lvEntries.Columns.Count)
5133 				{
5134 					int[] vDisplayIndices = new int[l.Count];
5135 					for(int i = 0; i < l.Count; ++i)
5136 					{
5137 						l[i].Width = m_lvEntries.Columns[i].Width;
5138 						vDisplayIndices[i] = m_lvEntries.Columns[i].DisplayIndex;
5139 					}
5140 
5141 					Program.Config.MainWindow.EntryListColumnDisplayOrder =
5142 						StrUtil.SerializeIntArray(vDisplayIndices);
5143 				}
5144 				else { Debug.Assert(false); }
5145 			}
5146 
5147 			m_bBlockColumnUpdates = false;
5148 		}
5149 
EntryListAddColumn(AceColumnType t, int nWidth, bool bHide)5150 		private static void EntryListAddColumn(AceColumnType t, int nWidth, bool bHide)
5151 		{
5152 			AceColumn c = new AceColumn(t, string.Empty, bHide, nWidth);
5153 			Program.Config.MainWindow.EntryListColumns.Add(c);
5154 		}
5155 
GetEntryFieldEx(PwEntry pe, int iColumnID, bool bFormatForDisplay, out bool bRequestAsync)5156 		private string GetEntryFieldEx(PwEntry pe, int iColumnID,
5157 			bool bFormatForDisplay, out bool bRequestAsync)
5158 		{
5159 			List<AceColumn> l = Program.Config.MainWindow.EntryListColumns;
5160 			if((iColumnID < 0) || (iColumnID >= l.Count))
5161 			{
5162 				Debug.Assert(false);
5163 				bRequestAsync = false;
5164 				return string.Empty;
5165 			}
5166 
5167 			AceColumn col = l[iColumnID];
5168 			if(bFormatForDisplay && col.HideWithAsterisks)
5169 			{
5170 				bRequestAsync = false;
5171 				return PwDefs.HiddenPassword;
5172 			}
5173 
5174 			string str;
5175 			switch(col.Type)
5176 			{
5177 				case AceColumnType.Title: str = pe.Strings.ReadSafe(PwDefs.TitleField); break;
5178 				case AceColumnType.UserName: str = pe.Strings.ReadSafe(PwDefs.UserNameField); break;
5179 				case AceColumnType.Password: str = pe.Strings.ReadSafe(PwDefs.PasswordField); break;
5180 				case AceColumnType.Url: str = pe.Strings.ReadSafe(PwDefs.UrlField); break;
5181 				case AceColumnType.Notes:
5182 					str = pe.Strings.ReadSafe(PwDefs.NotesField);
5183 					if(bFormatForDisplay) str = StrUtil.MultiToSingleLine(str);
5184 					break;
5185 				case AceColumnType.CreationTime:
5186 					str = TimeUtil.ToDisplayString(pe.CreationTime);
5187 					break;
5188 				case AceColumnType.LastModificationTime:
5189 					str = TimeUtil.ToDisplayString(pe.LastModificationTime);
5190 					break;
5191 				case AceColumnType.LastAccessTime:
5192 					str = TimeUtil.ToDisplayString(pe.LastAccessTime);
5193 					break;
5194 				case AceColumnType.ExpiryTime:
5195 					if(pe.Expires) str = TimeUtil.ToDisplayString(pe.ExpiryTime);
5196 					else str = m_strNeverExpires;
5197 					break;
5198 				case AceColumnType.Uuid: str = pe.Uuid.ToHexString(); break;
5199 				case AceColumnType.Attachment: str = pe.Binaries.KeysToString(); break;
5200 				case AceColumnType.CustomString:
5201 					str = pe.Strings.ReadSafe(col.CustomName);
5202 					if(bFormatForDisplay) str = StrUtil.MultiToSingleLine(str);
5203 					break;
5204 				case AceColumnType.PluginExt:
5205 					str = Program.ColumnProviderPool.GetCellData(col.CustomName, pe);
5206 					if(bFormatForDisplay) str = StrUtil.MultiToSingleLine(str);
5207 					break;
5208 				case AceColumnType.OverrideUrl: str = pe.OverrideUrl; break;
5209 				case AceColumnType.Tags:
5210 					str = TagUtil.TagsInheritedToString(pe.Tags, pe.ParentGroup);
5211 					break;
5212 				case AceColumnType.ExpiryTimeDateOnly:
5213 					if(pe.Expires) str = TimeUtil.ToDisplayStringDateOnly(pe.ExpiryTime);
5214 					else str = m_strNeverExpires;
5215 					break;
5216 				case AceColumnType.Size:
5217 					str = StrUtil.FormatDataSizeKB(pe.GetSize());
5218 					break;
5219 				case AceColumnType.HistoryCount:
5220 					str = pe.History.UCount.ToString();
5221 					break;
5222 				case AceColumnType.AttachmentCount:
5223 					uint uc = pe.Binaries.UCount;
5224 					str = ((uc > 0) ? uc.ToString() : string.Empty);
5225 					break;
5226 				case AceColumnType.LastPasswordModTime:
5227 					str = TimeUtil.ToDisplayString(EntryUtil.GetLastPasswordModTime(pe));
5228 					break;
5229 				case AceColumnType.AutoTypeEnabled:
5230 					object oBlocker;
5231 					str = AutoType.GetEnabledText(pe, out oBlocker);
5232 					break;
5233 				case AceColumnType.AutoTypeSequences:
5234 					str = AutoType.GetSequencesText(pe);
5235 					break;
5236 				default: Debug.Assert(false); str = string.Empty; break;
5237 			}
5238 
5239 			if(Program.Config.MainWindow.EntryListShowDerefData && bFormatForDisplay)
5240 			{
5241 				switch(col.Type)
5242 				{
5243 					case AceColumnType.Title:
5244 					case AceColumnType.UserName:
5245 					case AceColumnType.Password:
5246 					case AceColumnType.Url:
5247 					case AceColumnType.Notes:
5248 					case AceColumnType.CustomString:
5249 						bRequestAsync = SprEngine.MightDeref(str);
5250 						break;
5251 					default: bRequestAsync = false; break;
5252 				}
5253 
5254 				if(!Program.Config.MainWindow.EntryListShowDerefDataAsync &&
5255 					bRequestAsync)
5256 				{
5257 					PwListItem pli = new PwListItem(pe);
5258 					str = AsyncPwListUpdate.SprCompileFn(str, pli);
5259 					bRequestAsync = false;
5260 				}
5261 			}
5262 			else bRequestAsync = false;
5263 
5264 			return str;
5265 		}
5266 
GetAceColumn(int nColID)5267 		private static AceColumn GetAceColumn(int nColID)
5268 		{
5269 			List<AceColumn> v = Program.Config.MainWindow.EntryListColumns;
5270 			if((nColID < 0) || (nColID >= v.Count)) { Debug.Assert(false); return new AceColumn(); }
5271 
5272 			return v[nColID];
5273 		}
5274 
ToggleFieldAsterisks(AceColumnType colType)5275 		private void ToggleFieldAsterisks(AceColumnType colType)
5276 		{
5277 			List<AceColumn> l = Program.Config.MainWindow.EntryListColumns;
5278 			foreach(AceColumn c in l)
5279 			{
5280 				if(c.Type == colType)
5281 				{
5282 					if((colType == AceColumnType.Password) && c.HideWithAsterisks &&
5283 						!AppPolicy.Try(AppPolicyId.UnhidePasswords))
5284 						return;
5285 
5286 					c.HideWithAsterisks = !c.HideWithAsterisks;
5287 				}
5288 			}
5289 
5290 			RefreshEntriesList();
5291 			UpdateUIState(false); // Update entry view
5292 		}
5293 
EditSelectedEntry(PwEntryFormTab eftInit)5294 		private void EditSelectedEntry(PwEntryFormTab eftInit)
5295 		{
5296 			PwDatabase pd = m_docMgr.ActiveDatabase;
5297 			if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; }
5298 
5299 			MultipleValuesEntryContext mvec = null;
5300 			bool bMod;
5301 			try
5302 			{
5303 				PwEntry pe;
5304 				PwEntry[] v = GetSelectedEntries();
5305 				if((v == null) || (v.Length <= 1))
5306 				{
5307 					pe = GetSelectedEntry(false);
5308 					if(pe == null) return; // Do not assert
5309 				}
5310 				else
5311 				{
5312 					mvec = new MultipleValuesEntryContext(v, pd, out pe);
5313 
5314 					// if(pd.UINeedsIconUpdate)
5315 					// {
5316 					//	RefreshEntriesList();
5317 					//	UpdateUI(false, null, true, null, false, null, false);
5318 					// }
5319 					UpdateImageLists(false); // Image indices remain the same
5320 				}
5321 
5322 				PwEntryForm dlg = new PwEntryForm();
5323 				dlg.InitEx(pe, PwEditMode.EditExistingEntry, pd, m_ilCurrentIcons,
5324 					false, false);
5325 				dlg.InitialTab = eftInit;
5326 				dlg.MultipleValuesEntryContext = mvec;
5327 
5328 				bool bOK = (dlg.ShowDialog() == DialogResult.OK);
5329 				bMod = (bOK && dlg.HasModifiedEntry);
5330 				UIUtil.DestroyForm(dlg);
5331 
5332 				// Check bOK instead of bMod (dialog mod. check ignores multi states)
5333 				if(bOK && (mvec != null))
5334 				{
5335 					if(mvec.ApplyChanges()) bMod = true;
5336 				}
5337 			}
5338 			finally { if(mvec != null) mvec.Dispose(); }
5339 
5340 			bool bUpdImg = pd.UINeedsIconUpdate; // Refreshing entries resets it
5341 			RefreshEntriesList(); // Last access time
5342 			UpdateUI(false, null, bUpdImg, null, false, null, bMod);
5343 
5344 			if(Program.Config.Application.AutoSaveAfterEntryEdit && bMod)
5345 				SaveDatabase(pd, null);
5346 		}
5347 
EditSelectedGroup(GroupFormTab gftInit)5348 		private void EditSelectedGroup(GroupFormTab gftInit)
5349 		{
5350 			PwDatabase pd = m_docMgr.ActiveDatabase;
5351 			if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; }
5352 
5353 			PwGroup pg = GetSelectedGroup();
5354 			if(pg == null) { Debug.Assert(false); return; }
5355 
5356 			GroupForm gf = new GroupForm();
5357 			gf.InitEx(pg, false, m_ilCurrentIcons, pd);
5358 			gf.InitialTab = gftInit;
5359 
5360 			if(UIUtil.ShowDialogAndDestroy(gf) == DialogResult.OK)
5361 				UpdateUI(false, null, true, null, true, null, true);
5362 			else UpdateUI(false, null, pd.UINeedsIconUpdate, null,
5363 				pd.UINeedsIconUpdate, null, false);
5364 		}
5365 
ListContainsOnlyTans(PwObjectList<PwEntry> vEntries)5366 		private static bool ListContainsOnlyTans(PwObjectList<PwEntry> vEntries)
5367 		{
5368 			if(vEntries == null) { Debug.Assert(false); return true; }
5369 
5370 			foreach(PwEntry pe in vEntries)
5371 			{
5372 				if(!PwDefs.IsTanEntry(pe)) return false;
5373 			}
5374 
5375 			return true;
5376 		}
5377 
PerformSearch(SearchParameters sp, bool bInGroup, bool bFocusEntryList)5378 		private void PerformSearch(SearchParameters sp, bool bInGroup,
5379 			bool bFocusEntryList)
5380 		{
5381 			if(sp == null) { Debug.Assert(false); return; }
5382 
5383 			PwDatabase pd = m_docMgr.ActiveDatabase;
5384 			if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; }
5385 
5386 			PwGroup pgRoot = (bInGroup ? GetSelectedGroup() : null);
5387 			if(pgRoot == null) pgRoot = pd.RootGroup;
5388 			if(pgRoot == null) { Debug.Assert(false); return; }
5389 
5390 			Application.DoEvents(); // Handle UI messages before blocking
5391 			UIBlockInteraction(true);
5392 			StatusBarLogger sl = CreateStatusBarLogger();
5393 			sl.StartLogging(KPRes.SearchingOp + "...", false);
5394 
5395 			PwGroup pgResults = null;
5396 			Exception exFind = null;
5397 			try { pgResults = SearchUtil.Find(sp, pgRoot, sl); }
5398 			catch(Exception ex) { exFind = ex; }
5399 
5400 			sl.EndLogging();
5401 			UIBlockInteraction(false);
5402 
5403 			if(exFind != null)
5404 				MessageService.ShowWarning(sp.SearchString, exFind);
5405 			else ShowSearchResults(pgResults, sp, pgRoot, bFocusEntryList);
5406 		}
5407 
PerformSearchDialog(string strProfile, bool bInGroup)5408 		private void PerformSearchDialog(string strProfile, bool bInGroup)
5409 		{
5410 			PwDatabase pd = m_docMgr.ActiveDatabase;
5411 			if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; }
5412 
5413 			PwGroup pgRoot = (bInGroup ? GetSelectedGroup() : null);
5414 			if(pgRoot == null) pgRoot = pd.RootGroup;
5415 			if(pgRoot == null) { Debug.Assert(false); return; }
5416 
5417 			SearchForm sf = new SearchForm();
5418 			sf.InitEx(pd, pgRoot);
5419 			sf.InitProfile = strProfile;
5420 
5421 			if(sf.ShowDialog() == DialogResult.OK)
5422 				ShowSearchResults(sf.SearchResultsGroup, sf.SearchResultParameters,
5423 					pgRoot, true);
5424 			UIUtil.DestroyForm(sf);
5425 		}
5426 
5427 		private static string[] g_vProfileCmdTexts = null;
UpdateFindProfilesMenu(ToolStripMenuItem tsmiProfiles, bool bEnsurePopupOnly)5428 		private void UpdateFindProfilesMenu(ToolStripMenuItem tsmiProfiles,
5429 			bool bEnsurePopupOnly)
5430 		{
5431 			if(tsmiProfiles == null) { Debug.Assert(false); return; }
5432 
5433 			ToolStripItemCollection tsic = tsmiProfiles.DropDownItems;
5434 			tsic.Clear();
5435 
5436 			if(bEnsurePopupOnly)
5437 			{
5438 				tsic.Add(new ToolStripMenuItem(KPRes.None));
5439 				return;
5440 			}
5441 
5442 			if(g_vProfileCmdTexts == null)
5443 			{
5444 				List<char> lCmdAvailKeys = new List<char>(PwCharSet.MenuAccels);
5445 				GFunc<string, string, bool, string> f = delegate(string str,
5446 					string strSuffix, bool bDots)
5447 				{
5448 					str = StrUtil.TrimDots(StrUtil.RemoveAccelerator(str), true);
5449 					if(!string.IsNullOrEmpty(strSuffix))
5450 						str += " (" + StrUtil.EncodeMenuText(strSuffix) + ")";
5451 					if(bDots) str += "...";
5452 					return StrUtil.AddAccelerator(str, lCmdAvailKeys);
5453 				};
5454 
5455 				g_vProfileCmdTexts = new string[4];
5456 				g_vProfileCmdTexts[0] = f(KPRes.Find, null, false);
5457 				g_vProfileCmdTexts[1] = f(KPRes.Find, KPRes.SelectedGroup, false);
5458 				g_vProfileCmdTexts[2] = f(KPRes.OpenCmd, null, true);
5459 				g_vProfileCmdTexts[3] = f(KPRes.OpenCmd, KPRes.SelectedGroup, true);
5460 			}
5461 
5462 			List<ToolStripItem> lTsi = new List<ToolStripItem>();
5463 			List<char> lPrfAvailKeys = new List<char>(PwCharSet.MenuAccels);
5464 
5465 			AceSearch aceSearch = Program.Config.Search;
5466 			aceSearch.SortProfiles();
5467 
5468 			foreach(SearchParameters sp in aceSearch.UserProfiles)
5469 			{
5470 				ToolStripMenuItem tsmi = new ToolStripMenuItem(
5471 					StrUtil.AddAccelerator(StrUtil.EncodeMenuText(sp.Name),
5472 					lPrfAvailKeys));
5473 				lTsi.Add(tsmi);
5474 
5475 				ToolStripMenuItem tsmiFD = new ToolStripMenuItem(
5476 					g_vProfileCmdTexts[0], Properties.Resources.B16x16_XMag,
5477 					this.OnFindProfileFind);
5478 				tsmiFD.Tag = sp.Name;
5479 				tsmi.DropDownItems.Add(tsmiFD);
5480 
5481 				ToolStripMenuItem tsmiFG = new ToolStripMenuItem(
5482 					g_vProfileCmdTexts[1], Properties.Resources.B16x16_XMag,
5483 					this.OnFindProfileFindInGroup);
5484 				tsmiFG.Tag = sp.Name;
5485 				tsmi.DropDownItems.Add(tsmiFG);
5486 
5487 				tsmi.DropDownItems.Add(new ToolStripSeparator());
5488 
5489 				ToolStripMenuItem tsmiOD = new ToolStripMenuItem(
5490 					g_vProfileCmdTexts[2], Properties.Resources.B16x16_Misc,
5491 					this.OnFindProfileOpen);
5492 				tsmiOD.Tag = sp.Name;
5493 				tsmi.DropDownItems.Add(tsmiOD);
5494 
5495 				ToolStripMenuItem tsmiOG = new ToolStripMenuItem(
5496 					g_vProfileCmdTexts[3], Properties.Resources.B16x16_Misc,
5497 					this.OnFindProfileOpenInGroup);
5498 				tsmiOG.Tag = sp.Name;
5499 				tsmi.DropDownItems.Add(tsmiOG);
5500 			}
5501 
5502 			if(lTsi.Count == 0)
5503 			{
5504 				ToolStripMenuItem tsmi = new ToolStripMenuItem(
5505 					StrUtil.EncodeMenuText("(" + KPRes.None + ")"));
5506 				tsmi.Enabled = false;
5507 				lTsi.Add(tsmi);
5508 			}
5509 
5510 			tsic.AddRange(lTsi.ToArray());
5511 		}
5512 
PerformSearchMenu(object sender, bool bInGroup, bool bOpen)5513 		private void PerformSearchMenu(object sender, bool bInGroup, bool bOpen)
5514 		{
5515 			ToolStripMenuItem tsmi = (sender as ToolStripMenuItem);
5516 			if(tsmi == null) { Debug.Assert(false); return; }
5517 
5518 			string strProfile = (tsmi.Tag as string);
5519 			if(string.IsNullOrEmpty(strProfile)) { Debug.Assert(false); return; }
5520 
5521 			if(bOpen) PerformSearchDialog(strProfile, bInGroup);
5522 			else
5523 			{
5524 				SearchParameters sp = Program.Config.Search.FindProfile(strProfile);
5525 				PerformSearch(sp, bInGroup, true);
5526 			}
5527 		}
5528 
OnFindProfileFind(object sender, EventArgs e)5529 		private void OnFindProfileFind(object sender, EventArgs e)
5530 		{
5531 			PerformSearchMenu(sender, false, false);
5532 		}
5533 
OnFindProfileFindInGroup(object sender, EventArgs e)5534 		private void OnFindProfileFindInGroup(object sender, EventArgs e)
5535 		{
5536 			PerformSearchMenu(sender, true, false);
5537 		}
5538 
OnFindProfileOpen(object sender, EventArgs e)5539 		private void OnFindProfileOpen(object sender, EventArgs e)
5540 		{
5541 			PerformSearchMenu(sender, false, true);
5542 		}
5543 
OnFindProfileOpenInGroup(object sender, EventArgs e)5544 		private void OnFindProfileOpenInGroup(object sender, EventArgs e)
5545 		{
5546 			PerformSearchMenu(sender, true, true);
5547 		}
5548 
OnShowEntriesByTag(object sender, DynamicMenuEventArgs e)5549 		private void OnShowEntriesByTag(object sender, DynamicMenuEventArgs e)
5550 		{
5551 			if(e == null) { Debug.Assert(false); return; }
5552 			ShowEntriesByTag((e.Tag as string), true);
5553 		}
5554 
ShowEntriesByTag(string strTag, bool bFocusEntryList)5555 		internal void ShowEntriesByTag(string strTag, bool bFocusEntryList)
5556 		{
5557 			if(strTag == null) { Debug.Assert(false); return; }
5558 			if(strTag.Length == 0) return; // No assert (call from trigger)
5559 
5560 			PwDatabase pd = m_docMgr.ActiveDatabase;
5561 			if((pd == null) || !pd.IsOpen) return; // No assert (call from trigger)
5562 
5563 			PwObjectList<PwEntry> l = new PwObjectList<PwEntry>();
5564 			pd.RootGroup.FindEntriesByTag(strTag, l, true);
5565 
5566 			PwGroup pgResults = new PwGroup(true, true);
5567 			pgResults.IsVirtual = true;
5568 			foreach(PwEntry pe in l) pgResults.AddEntry(pe, false, false);
5569 
5570 			ShowSearchResults(pgResults, null, pd.RootGroup, bFocusEntryList);
5571 		}
5572 
5573 		private enum TagsMenuMode
5574 		{
5575 			All = 0,
5576 			Add = 1,
5577 			Remove = 2,
5578 
5579 			EnsurePopupOnly = 127 // Ensure drawing of menu popup arrow
5580 		}
5581 
UpdateTagsMenu(DynamicMenu dm, bool bWithSeparator, bool bPrefixTag, TagsMenuMode tmm)5582 		private void UpdateTagsMenu(DynamicMenu dm, bool bWithSeparator, bool bPrefixTag,
5583 			TagsMenuMode tmm)
5584 		{
5585 			if(dm == null) { Debug.Assert(false); return; }
5586 			dm.Clear();
5587 
5588 			if(bWithSeparator) dm.AddSeparator();
5589 
5590 			string strNoTags = "(" + KPRes.TagsNotFound + ")";
5591 			PwDatabase pd = m_docMgr.ActiveDatabase;
5592 			if(!pd.IsOpen || (tmm == TagsMenuMode.EnsurePopupOnly))
5593 			{
5594 				ToolStripMenuItem tsmi = dm.AddItem(strNoTags, null, string.Empty);
5595 				tsmi.Enabled = false;
5596 				return;
5597 			}
5598 
5599 			bool bReqEntrySel = ((tmm == TagsMenuMode.Add) || (tmm == TagsMenuMode.Remove));
5600 			PwGroup pgSel = GetSelectedEntriesAsGroup();
5601 			uint uSelCount = pgSel.Entries.UCount;
5602 			bool bForceDisabled = (bReqEntrySel && (uSelCount == 0));
5603 
5604 			IDictionary<string, uint> dAllTags = pd.RootGroup.BuildEntryTagsDict(false);
5605 			List<string> lAllTags = new List<string>(dAllTags.Keys);
5606 			lAllTags.Sort(StrUtil.CompareNaturally);
5607 
5608 			Dictionary<string, bool> dEnabledTags = null;
5609 			if((tmm == TagsMenuMode.Add) && (uSelCount > 0))
5610 			{
5611 				dEnabledTags = new Dictionary<string, bool>();
5612 				List<string> lIntersect = pgSel.Entries.GetAt(0).Tags;
5613 				for(uint u = 1; u < uSelCount; ++u)
5614 					lIntersect = new List<string>(MemUtil.Intersect(lIntersect,
5615 						pgSel.Entries.GetAt(u).Tags, null));
5616 				foreach(string strTag in MemUtil.Except(lAllTags, lIntersect, null))
5617 					dEnabledTags[strTag] = true;
5618 			}
5619 			else if(tmm == TagsMenuMode.Remove)
5620 			{
5621 				dEnabledTags = new Dictionary<string, bool>();
5622 				List<string> lSelectedTags = pgSel.BuildEntryTagsList(false, false);
5623 				foreach(string strTag in lSelectedTags)
5624 					dEnabledTags[strTag] = true;
5625 			}
5626 
5627 			string strPrefix = StrUtil.EncodeMenuText(KPRes.Tag + ": ");
5628 			Image imgIcon = Properties.Resources.B16x16_KNotes;
5629 
5630 			List<char> lAvailKeys = new List<char>(PwCharSet.MenuAccels);
5631 			foreach(string strTag in lAllTags)
5632 			{
5633 				string strText = StrUtil.EncodeMenuText(strTag);
5634 				strText = StrUtil.AddAccelerator(strText, lAvailKeys);
5635 				if(bPrefixTag) strText = strPrefix + strText;
5636 
5637 				ToolStripMenuItem tsmi = dm.AddItem(strText, imgIcon, strTag);
5638 				if(tmm == TagsMenuMode.All)
5639 				{
5640 					uint uCount;
5641 					dAllTags.TryGetValue(strTag, out uCount);
5642 					tsmi.ShortcutKeyDisplayString = "(" + uCount.ToString() + ")";
5643 				}
5644 
5645 				if(bForceDisabled) tsmi.Enabled = false;
5646 				else if(dEnabledTags != null)
5647 				{
5648 					if(!dEnabledTags.ContainsKey(strTag)) tsmi.Enabled = false;
5649 				}
5650 			}
5651 
5652 			if(lAllTags.Count == 0)
5653 			{
5654 				ToolStripMenuItem tsmi = dm.AddItem(strNoTags, null, string.Empty);
5655 				tsmi.Enabled = false;
5656 			}
5657 		}
5658 
OnAddEntryTag(object sender, DynamicMenuEventArgs e)5659 		private void OnAddEntryTag(object sender, DynamicMenuEventArgs e)
5660 		{
5661 			string strTag = (e.Tag as string);
5662 			if(strTag == null) { Debug.Assert(false); return; }
5663 
5664 			if(strTag.Length == 0)
5665 			{
5666 				SingleLineEditForm dlg = new SingleLineEditForm();
5667 				dlg.InitEx(KPRes.TagNew, KPRes.TagAddNew, KPRes.Name + ":",
5668 					Properties.Resources.B48x48_KMag, string.Empty, null);
5669 
5670 				if(UIUtil.ShowDialogNotValue(dlg, DialogResult.OK)) return;
5671 				strTag = dlg.ResultString;
5672 				UIUtil.DestroyForm(dlg);
5673 			}
5674 
5675 			AddOrRemoveTagsToFromSelectedEntries(strTag, true);
5676 		}
5677 
OnRemoveEntryTag(object sender, DynamicMenuEventArgs e)5678 		private void OnRemoveEntryTag(object sender, DynamicMenuEventArgs e)
5679 		{
5680 			AddOrRemoveTagsToFromSelectedEntries((e.Tag as string), false);
5681 		}
5682 
AddOrRemoveTagsToFromSelectedEntries(string strTags, bool bAdd)5683 		private void AddOrRemoveTagsToFromSelectedEntries(string strTags, bool bAdd)
5684 		{
5685 			if(strTags == null) { Debug.Assert(false); return; }
5686 			if(strTags.Length == 0) return;
5687 
5688 			PwDatabase pd = m_docMgr.ActiveDatabase;
5689 			PwEntry[] vEntries = GetSelectedEntries();
5690 			if((vEntries == null) || (vEntries.Length == 0)) return;
5691 
5692 			List<string> lToProcess = StrUtil.StringToTags(strTags);
5693 			bool bModified = false;
5694 
5695 			foreach(PwEntry pe in vEntries)
5696 			{
5697 				List<string> l = pe.Tags;
5698 				int cBefore = l.Count;
5699 
5700 				if(bAdd)
5701 					l = new List<string>(MemUtil.Union<string>(l, lToProcess, null));
5702 				else
5703 					l = new List<string>(MemUtil.Except<string>(l, lToProcess, null));
5704 
5705 				if(l.Count != cBefore)
5706 				{
5707 					pe.CreateBackup(pd);
5708 
5709 					pe.Tags = l;
5710 					pe.Touch(true, false);
5711 
5712 					bModified = true;
5713 				}
5714 			}
5715 
5716 			if(bModified) RefreshEntriesList();
5717 			UpdateUIState(bModified);
5718 		}
5719 
5720 		private static bool? m_bCachedSelfTestResult = null;
PerformSelfTest()5721 		private static bool PerformSelfTest()
5722 		{
5723 			if(m_bCachedSelfTestResult.HasValue)
5724 				return m_bCachedSelfTestResult.Value;
5725 
5726 			bool bResult = true;
5727 			try { SelfTest.Perform(); }
5728 			catch(Exception exSelfTest)
5729 			{
5730 				MessageService.ShowWarning(KPRes.SelfTestFailed, exSelfTest);
5731 				bResult = false;
5732 			}
5733 
5734 			m_bCachedSelfTestResult = bResult;
5735 			return bResult;
5736 		}
5737 
SortSubGroups(bool bRecursive)5738 		private void SortSubGroups(bool bRecursive)
5739 		{
5740 			PwGroup pg = GetSelectedGroup();
5741 			if(pg == null) return;
5742 
5743 			if(!bRecursive && (pg.Groups.UCount <= 1)) return; // Nothing to do
5744 			if(pg.Groups.UCount == 0) return; // Nothing to do
5745 
5746 			pg.SortSubGroups(bRecursive);
5747 			UpdateUI(false, null, true, null, false, null, true);
5748 		}
5749 
5750 		/* private bool CreateColorizedIcon(Icon icoBase, int qSize,
5751 			ref KeyValuePair<Color, Icon> kvpStore, out Icon icoAssignable,
5752 			out Icon icoDisposable)
5753 		{
5754 			PwDatabase pd = m_docMgr.ActiveDatabase;
5755 
5756 			Color clrNew = Color.Empty;
5757 			if((pd != null) && pd.IsOpen && !UIUtil.ColorsEqual(pd.Color, Color.Empty))
5758 				clrNew = pd.Color;
5759 
5760 			if(UIUtil.ColorsEqual(clrNew, kvpStore.Key))
5761 			{
5762 				icoDisposable = null;
5763 				icoAssignable = (kvpStore.Value ?? icoBase);
5764 				return false;
5765 			}
5766 
5767 			icoDisposable = kvpStore.Value;
5768 			if(UIUtil.ColorsEqual(clrNew, Color.Empty))
5769 			{
5770 				kvpStore = new KeyValuePair<Color, Icon>(Color.Empty, null);
5771 				icoAssignable = icoBase;
5772 			}
5773 			else
5774 			{
5775 				kvpStore = new KeyValuePair<Color, Icon>(clrNew,
5776 					UIUtil.CreateColorizedIcon(icoBase, clrNew, qSize));
5777 				icoAssignable = kvpStore.Value;
5778 			}
5779 
5780 			return true;
5781 		} */
5782 
CreateColorizedIcon(AppIconType t, bool bSmall)5783 		private Icon CreateColorizedIcon(AppIconType t, bool bSmall)
5784 		{
5785 			PwDatabase pd = m_docMgr.ActiveDatabase;
5786 
5787 			Color clr = Color.Empty;
5788 			if((pd != null) && pd.IsOpen) clr = pd.Color;
5789 
5790 			Size sz = (bSmall ? UIUtil.GetSmallIconSize() : UIUtil.GetIconSize());
5791 
5792 			return AppIcons.Get(t, sz, clr);
5793 		}
5794 
SetObjectsDeletedStatus(uint uDeleted, bool bDbMntnc)5795 		private void SetObjectsDeletedStatus(uint uDeleted, bool bDbMntnc)
5796 		{
5797 			string str = (StrUtil.ReplaceCaseInsensitive(KPRes.ObjectsDeleted,
5798 				@"{PARAM}", uDeleted.ToString()) + ".");
5799 			SetStatusEx(str);
5800 			if(!bDbMntnc || !Program.Config.UI.ShowDbMntncResultsDialog) return;
5801 
5802 			VistaTaskDialog dlg = new VistaTaskDialog();
5803 			dlg.CommandLinks = false;
5804 			dlg.Content = str;
5805 			dlg.SetIcon(VtdIcon.Information);
5806 			dlg.VerificationText = KPRes.DialogNoShowAgain;
5807 			dlg.WindowTitle = PwDefs.ShortProductName;
5808 			if(dlg.ShowDialog())
5809 			{
5810 				if(dlg.ResultVerificationChecked)
5811 					Program.Config.UI.ShowDbMntncResultsDialog = false;
5812 			}
5813 			else MessageService.ShowInfo(str);
5814 		}
5815 
ApplyUICustomizations()5816 		private void ApplyUICustomizations()
5817 		{
5818 			ulong u = Program.Config.UI.UIFlags;
5819 			if((u & (ulong)AceUIFlags.DisableOptions) != 0)
5820 				m_menuToolsOptions.Enabled = false;
5821 			if((u & (ulong)AceUIFlags.DisablePlugins) != 0)
5822 				m_menuToolsPlugins.Enabled = false;
5823 			if((u & (ulong)AceUIFlags.DisableTriggers) != 0)
5824 				m_menuToolsTriggers.Enabled = false;
5825 			if((u & (ulong)AceUIFlags.DisableUpdateCheck) != 0)
5826 				m_menuHelpCheckForUpdates.Enabled = false;
5827 		}
5828 
OnFormLoadParallelAsync(object stateInfo)5829 		private static void OnFormLoadParallelAsync(object stateInfo)
5830 		{
5831 			try
5832 			{
5833 				PopularPasswords.Add(Properties.Resources.MostPopularPasswords, true);
5834 
5835 				string strShInstUtil = UrlUtil.GetFileDirectory(
5836 					WinUtil.GetExecutable(), true, false) + AppDefs.FileNames.ShInstUtil;
5837 
5838 				// Unblock the application such that the user isn't
5839 				// prompted next time anymore
5840 				WinUtil.RemoveZoneIdentifier(WinUtil.GetExecutable());
5841 				WinUtil.RemoveZoneIdentifier(AppHelp.LocalHelpFile);
5842 				WinUtil.RemoveZoneIdentifier(strShInstUtil);
5843 
5844 				// https://stackoverflow.com/questions/26256917/how-can-i-prevent-my-application-from-causing-a-0xc0000142-error-in-csc-exe
5845 				XmlSerializer xs = new XmlSerializer(typeof(IpcParamEx));
5846 				IpcParamEx ipc = new IpcParamEx();
5847 				using(MemoryStream ms = new MemoryStream())
5848 				{
5849 					xs.Serialize(ms, ipc);
5850 				}
5851 
5852 				FileTransactionEx.ClearOld();
5853 			}
5854 			catch(Exception) { Debug.Assert(false); }
5855 		}
5856 
IsCommandTypeInvokable(MainAppState sContext, AppCommandType t)5857 		internal bool IsCommandTypeInvokable(MainAppState sContext, AppCommandType t)
5858 		{
5859 			MainAppState s = (sContext ?? GetMainAppState());
5860 
5861 			if(t == AppCommandType.Window)
5862 				return (s.NoWindowShown && !UIIsInteractionBlocked());
5863 			if(t == AppCommandType.Lock)
5864 				return (s.NoWindowShown && !UIIsInteractionBlocked() && s.EnableLockCmd);
5865 
5866 			return false;
5867 		}
5868 
UpdateTrayState()5869 		private void UpdateTrayState()
5870 		{
5871 			UpdateUIState(false); // Update tray lock text
5872 
5873 			ulong u = Program.Config.UI.UIFlags;
5874 			bool bOptions = ((u & (ulong)AceUIFlags.DisableOptions) == 0);
5875 
5876 			MainAppState s = GetMainAppState();
5877 			bool bInvkWnd = IsCommandTypeInvokable(s, AppCommandType.Window);
5878 
5879 			m_ctxTrayTray.Enabled = bInvkWnd;
5880 			m_ctxTrayGenPw.Enabled = bInvkWnd;
5881 			m_ctxTrayOptions.Enabled = (bInvkWnd && bOptions);
5882 			m_ctxTrayCancel.Enabled = (m_sCancellable.Count != 0);
5883 			m_ctxTrayLock.Enabled = IsCommandTypeInvokable(s, AppCommandType.Lock);
5884 			m_ctxTrayFileExit.Enabled = bInvkWnd;
5885 		}
5886 
GetEntryListSprContext(PwEntry pe, PwDatabase pd)5887 		internal static SprContext GetEntryListSprContext(PwEntry pe,
5888 			PwDatabase pd)
5889 		{
5890 			SprContext ctx = new SprContext(pe, pd, SprCompileFlags.Deref);
5891 			ctx.ForcePlainTextPasswords = false;
5892 			return ctx;
5893 		}
5894 
EnsureAlwaysOnTopOpt()5895 		private void EnsureAlwaysOnTopOpt()
5896 		{
5897 			bool bWish = Program.Config.MainWindow.AlwaysOnTop;
5898 			if(NativeLib.IsUnix()) { this.TopMost = bWish; return; }
5899 
5900 			// Workaround for issue reported in KPB 3475997
5901 			this.TopMost = false;
5902 			if(bWish) this.TopMost = true;
5903 		}
5904 
IsPrimaryControlActive()5905 		private bool IsPrimaryControlActive()
5906 		{
5907 			try
5908 			{
5909 				// return (m_lvEntries.Focused || m_tvGroups.Focused ||
5910 				//	m_richEntryView.Focused || m_tbQuickFind.Focused);
5911 
5912 				Control c = UIUtil.GetActiveControl(this);
5913 				if(c == null) return false;
5914 
5915 				return ((c == m_lvEntries) || (c == m_tvGroups) ||
5916 					(c == m_richEntryView) || (c == m_tbQuickFind.Control));
5917 			}
5918 			catch(Exception) { Debug.Assert(false); }
5919 
5920 			return false;
5921 		}
5922 
SetVisibleCore(bool value)5923 		protected override void SetVisibleCore(bool value)
5924 		{
5925 			if(MonoWorkarounds.IsRequired(3574233558U))
5926 			{
5927 				if(!value && m_bFormShown && (this.WindowState ==
5928 					FormWindowState.Minimized))
5929 				{
5930 					// Mono destroys window when trying to hide minimized
5931 					// window; so, restore it before hiding
5932 					this.WindowState = FormWindowState.Normal;
5933 					Application.DoEvents();
5934 					Thread.Sleep(250);
5935 				}
5936 
5937 				if(m_bFormShown) m_menuMain.Visible = value;
5938 			}
5939 
5940 			base.SetVisibleCore(value);
5941 		}
5942 
MoveOrCopySelectedEntries(PwGroup pgTo, DragDropEffects e)5943 		private void MoveOrCopySelectedEntries(PwGroup pgTo, DragDropEffects e)
5944 		{
5945 			PwEntry[] vSelected = GetSelectedEntries();
5946 			if((vSelected == null) || (vSelected.Length == 0)) return;
5947 
5948 			PwGroup pgSafeView = (m_pgActiveAtDragStart ?? new PwGroup());
5949 			bool bFullUpdateView = false;
5950 			List<PwEntry> vNowInvisible = new List<PwEntry>();
5951 
5952 			if(e == DragDropEffects.Move)
5953 			{
5954 				foreach(PwEntry pe in vSelected)
5955 				{
5956 					PwGroup pgParent = pe.ParentGroup;
5957 					if(pgParent == null) { Debug.Assert(false); continue; }
5958 					if(pgParent == pgTo) continue;
5959 
5960 					if(!pgParent.Entries.Remove(pe)) { Debug.Assert(false); continue; }
5961 
5962 					pgTo.AddEntry(pe, true, true);
5963 
5964 					// pe.CreateBackup(m_docMgr.ActiveDatabase);
5965 					pe.PreviousParentGroup = pgParent.Uuid;
5966 					// pe.Touch(true, false);
5967 
5968 					if(pe.IsContainedIn(pgSafeView)) bFullUpdateView = true;
5969 					else vNowInvisible.Add(pe);
5970 				}
5971 			}
5972 			else if(e == DragDropEffects.Copy)
5973 			{
5974 				foreach(PwEntry pe in vSelected)
5975 				{
5976 					PwEntry peCopy = pe.Duplicate();
5977 
5978 					pgTo.AddEntry(peCopy, true, true);
5979 
5980 					if(peCopy.IsContainedIn(pgSafeView)) bFullUpdateView = true;
5981 				}
5982 			}
5983 			else { Debug.Assert(false); }
5984 
5985 			if(!bFullUpdateView)
5986 			{
5987 				RemoveEntriesFromList(vNowInvisible, true);
5988 				UpdateUI(false, null, true, m_pgActiveAtDragStart, false, null, true);
5989 			}
5990 			else UpdateUI(false, null, true, m_pgActiveAtDragStart, true, null, true);
5991 
5992 			m_pgActiveAtDragStart = null;
5993 		}
5994 
UpdateEntryMoveMenu(DynamicMenu dm, bool bDummyOnly)5995 		private void UpdateEntryMoveMenu(DynamicMenu dm, bool bDummyOnly)
5996 		{
5997 			if(dm == null) { Debug.Assert(false); return; }
5998 
5999 			dm.Clear();
6000 
6001 			PwDatabase pd = m_docMgr.ActiveDatabase;
6002 			PwGroup pgRoot = pd.RootGroup;
6003 			if((pd == null) || !pd.IsOpen || (pgRoot == null) || bDummyOnly)
6004 			{
6005 				ToolStripMenuItem tsmi = dm.AddItem(m_strNoneP, null);
6006 				tsmi.Enabled = false;
6007 				return;
6008 			}
6009 
6010 			List<char> lAvailKeys = new List<char>(PwCharSet.MenuAccels);
6011 			GroupHandler gh = delegate(PwGroup pg)
6012 			{
6013 				string strName = StrUtil.EncodeMenuText(pg.Name);
6014 				strName = StrUtil.AddAccelerator(strName, lAvailKeys);
6015 				strName = strName.PadLeft(((int)pg.GetDepth() * 4) + strName.Length);
6016 
6017 				int nIconID = ((!pg.CustomIconUuid.Equals(PwUuid.Zero)) ?
6018 					((int)PwIcon.Count + pd.GetCustomIconIndex(
6019 					pg.CustomIconUuid)) : (int)pg.IconId);
6020 
6021 				ToolStripMenuItem tsmi = dm.AddItem(strName,
6022 					m_ilCurrentIcons.Images[nIconID], pg);
6023 
6024 				if(pd.RecycleBinEnabled && pg.Uuid.Equals(pd.RecycleBinUuid) &&
6025 					(m_fontItalicTree != null))
6026 					tsmi.Font = m_fontItalicTree;
6027 
6028 				return true;
6029 			};
6030 
6031 			gh(pgRoot);
6032 			pgRoot.TraverseTree(TraversalMethod.PreOrder, gh, null);
6033 		}
6034 
OnEntryMoveToGroup(object sender, DynamicMenuEventArgs e)6035 		private void OnEntryMoveToGroup(object sender, DynamicMenuEventArgs e)
6036 		{
6037 			PwGroup pgTo = (e.Tag as PwGroup);
6038 			if(pgTo == null) { Debug.Assert(false); return; }
6039 
6040 			m_pgActiveAtDragStart = GetSelectedGroup();
6041 			MoveOrCopySelectedEntries(pgTo, DragDropEffects.Move);
6042 		}
6043 
FixDuplicateUuids(PwDatabase pd, IOConnectionInfo ioc)6044 		private bool FixDuplicateUuids(PwDatabase pd, IOConnectionInfo ioc)
6045 		{
6046 			if(pd == null) { Debug.Assert(false); return false; }
6047 
6048 			if(!pd.HasDuplicateUuids()) return false;
6049 
6050 			string str = string.Empty;
6051 			if(ioc != null)
6052 			{
6053 				string strFile = ioc.GetDisplayName();
6054 				if(!string.IsNullOrEmpty(strFile))
6055 					str += strFile + MessageService.NewParagraph;
6056 			}
6057 
6058 			str += KPRes.UuidDupInDb + MessageService.NewParagraph +
6059 				KPRes.CorruptionByExt + MessageService.NewParagraph +
6060 				KPRes.UuidFix;
6061 
6062 			if(VistaTaskDialog.ShowMessageBoxEx(str, null,
6063 				PwDefs.ShortProductName, VtdIcon.Warning, null,
6064 				KPRes.RepairCmd, (int)DialogResult.Cancel, null, 0) < 0)
6065 				MessageService.ShowWarning(str);
6066 
6067 			pd.FixDuplicateUuids();
6068 			pd.Modified = true;
6069 			return true;
6070 		}
6071 
BlockMainTimer(bool bBlock)6072 		private void BlockMainTimer(bool bBlock)
6073 		{
6074 			if(bBlock)
6075 			{
6076 				m_timerMain.Enabled = false;
6077 				++m_uMainTimerBlocked;
6078 			}
6079 			else
6080 			{
6081 				if(m_uMainTimerBlocked == 0) { Debug.Assert(false); return; }
6082 
6083 				m_timerMain.Enabled = true;
6084 				--m_uMainTimerBlocked;
6085 			}
6086 		}
6087 
ShowSelectedEntryParentGroup()6088 		private void ShowSelectedEntryParentGroup()
6089 		{
6090 			PwEntry pe = GetSelectedEntry(false);
6091 			if(pe == null) return;
6092 
6093 			PwGroup pg = pe.ParentGroup;
6094 			if(pg == null) { Debug.Assert(false); return; }
6095 
6096 			UpdateUI(false, null, true, pg, true, null, false);
6097 
6098 			TreeNode tnSel = m_tvGroups.SelectedNode;
6099 			if(tnSel != null) tnSel.EnsureVisible();
6100 
6101 			EnsureVisibleSelected(false);
6102 		}
6103 
PerformAutoType(string strSeq)6104 		private void PerformAutoType(string strSeq)
6105 		{
6106 			PwEntry pe = GetSelectedEntry(false);
6107 			if(pe != null)
6108 			{
6109 				try
6110 				{
6111 					AutoType.PerformIntoPreviousWindow(this, pe,
6112 						m_docMgr.SafeFindContainerOf(pe), strSeq);
6113 				}
6114 				catch(Exception ex) { MessageService.ShowWarning(ex); }
6115 			}
6116 		}
6117 
OnEntryPerformAutoTypeAdv(object sender, DynamicMenuEventArgs e)6118 		private void OnEntryPerformAutoTypeAdv(object sender, DynamicMenuEventArgs e)
6119 		{
6120 			if(e == null) { Debug.Assert(false); return; }
6121 
6122 			string strSeq = e.ItemName;
6123 			if(string.IsNullOrEmpty(strSeq)) { Debug.Assert(false); return; }
6124 
6125 			PerformAutoType(strSeq);
6126 		}
6127 
UpdateAlternatingBgColor()6128 		private void UpdateAlternatingBgColor()
6129 		{
6130 			m_clrAlternateItemBgColor = UIUtil.GetAlternateColorEx(m_lvEntries.BackColor);
6131 		}
6132 
CreateAndShowEntryList(EntryReportDelegate f, string strStatus, Image imgIcon, string strTitle, string strSubTitle, string strNote, bool bForceDialog, bool bDisableSorting)6133 		private int CreateAndShowEntryList(EntryReportDelegate f, string strStatus,
6134 			Image imgIcon, string strTitle, string strSubTitle, string strNote,
6135 			bool bForceDialog, bool bDisableSorting)
6136 		{
6137 			if(f == null) { Debug.Assert(false); return -1; }
6138 
6139 			PwDatabase pd = m_docMgr.ActiveDatabase;
6140 			if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return -1; }
6141 
6142 			Form fOptDialog;
6143 			IStatusLogger sl = StatusUtil.CreateStatusDialog(this, out fOptDialog,
6144 				null, (strStatus ?? "..."), true, false);
6145 			UIBlockInteraction(true);
6146 
6147 			Action<ListView> fInit = null;
6148 			List<object> l = null;
6149 
6150 			string strExcp = null;
6151 			try { l = f(pd, sl, out fInit); }
6152 			catch(Exception ex) { strExcp = ex.Message; }
6153 
6154 			UIBlockInteraction(false);
6155 			sl.EndLogging();
6156 
6157 			if(!string.IsNullOrEmpty(strExcp))
6158 				MessageService.ShowWarning(strExcp);
6159 
6160 			if(l == null) return -1;
6161 			if((l.Count == 0) && !bForceDialog) return 0;
6162 
6163 			ListViewForm dlg = new ListViewForm();
6164 			dlg.InitEx(strTitle, strSubTitle, strNote, imgIcon, l,
6165 				m_ilCurrentIcons, fInit);
6166 			UIUtil.ShowDialogAndDestroy(dlg, this);
6167 
6168 			PwGroup pg = (dlg.ResultGroup as PwGroup);
6169 			PwEntry pe = (dlg.ResultItem as PwEntry);
6170 			if((pg == null) && (pe == null))
6171 				pg = (dlg.ResultItem as PwGroup);
6172 
6173 			if((pg != null) || (pe != null))
6174 			{
6175 				if(pg != null)
6176 					UpdateUI(false, null, false, null, true, pg, false, m_lvEntries);
6177 				else
6178 					UpdateUI(false, null, true, pe.ParentGroup, true, null, false, m_lvEntries);
6179 
6180 				if(bDisableSorting) SortPasswordList(false, 0, null, true);
6181 
6182 				if(pe != null)
6183 				{
6184 					PwObjectList<PwEntry> lSel = new PwObjectList<PwEntry>();
6185 					lSel.Add(pe);
6186 					SelectEntries(lSel, true, true);
6187 
6188 					EnsureVisibleSelected(false);
6189 				}
6190 				else SelectFirstEntryIfNoneSelected();
6191 
6192 				UpdateUIState(false); // For selected entry
6193 			}
6194 
6195 			return l.Count;
6196 		}
6197 
CopySelectedObjects(Type t, bool bEncrypt)6198 		private void CopySelectedObjects(Type t, bool bEncrypt)
6199 		{
6200 			PwDatabase pd = m_docMgr.ActiveDatabase;
6201 			if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; }
6202 
6203 			if(!AppPolicy.Try(AppPolicyId.CopyWholeEntries)) return;
6204 
6205 			try
6206 			{
6207 				if(t == typeof(PwGroup))
6208 				{
6209 					PwGroup pg = GetSelectedGroup();
6210 					if(pg == null) { Debug.Assert(false); return; }
6211 
6212 					EntryUtil.CopyGroupToClipboard(pd, pg, this.Handle, bEncrypt);
6213 				}
6214 				else if(t == typeof(PwEntry))
6215 				{
6216 					PwEntry[] v = GetSelectedEntries();
6217 					if((v == null) || (v.Length == 0)) { Debug.Assert(false); return; }
6218 
6219 					EntryUtil.CopyEntriesToClipboard(pd, v, this.Handle, bEncrypt);
6220 				}
6221 				else { Debug.Assert(false); return; }
6222 
6223 				StartClipboardCountdown();
6224 			}
6225 			catch(Exception ex) { MessageService.ShowWarning(ex); }
6226 		}
6227 
SetSelectedEntryExpiry(bool bExpNow)6228 		private void SetSelectedEntryExpiry(bool bExpNow)
6229 		{
6230 			PwDatabase pd = m_docMgr.ActiveDatabase;
6231 			if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; }
6232 
6233 			PwEntry[] v = GetSelectedEntries();
6234 			if((v == null) || (v.Length == 0)) return;
6235 
6236 			DateTime dt = DateTime.UtcNow.AddSeconds(-1);
6237 			bool bMod = false;
6238 
6239 			foreach(PwEntry pe in v)
6240 			{
6241 				if(pe == null) { Debug.Assert(false); continue; }
6242 				if(!bExpNow && !pe.Expires) continue;
6243 
6244 				pe.CreateBackup(pd);
6245 
6246 				pe.Expires = bExpNow;
6247 				if(bExpNow) pe.ExpiryTime = dt;
6248 
6249 				pe.Touch(true, false);
6250 				bMod = true;
6251 			}
6252 
6253 			RefreshEntriesList();
6254 			UpdateUIState(bMod);
6255 		}
6256 
AddEntryEx(object sender, GFunc<PwEntry> fNewEntry, Action<PwEntry> fAddPre, Action<PwEntry> fAddPost)6257 		internal void AddEntryEx(object sender, GFunc<PwEntry> fNewEntry,
6258 			Action<PwEntry> fAddPre, Action<PwEntry> fAddPost)
6259 		{
6260 			PwDatabase pd = m_docMgr.ActiveDatabase;
6261 			if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; }
6262 
6263 			PwGroup pg = GetSelectedGroup();
6264 			if((pg == null) || pg.IsVirtual)
6265 			{
6266 				MessageService.ShowWarning(KPRes.GroupCannotStoreEntries,
6267 					KPRes.SelectDifferentGroup);
6268 				return;
6269 			}
6270 
6271 			PwEntry pe;
6272 			if(fNewEntry != null)
6273 			{
6274 				pe = fNewEntry();
6275 				if(pe == null) { Debug.Assert(false); return; }
6276 			}
6277 			else
6278 			{
6279 				pe = new PwEntry(true, true);
6280 
6281 				pe.IconId = PwDefs.GroupIconToEntryIcon(pg.IconId);
6282 				pe.CustomIconUuid = pg.CustomIconUuid;
6283 			}
6284 
6285 			// Temporarily assume that the entry is in pg; required for retrieving
6286 			// the default auto-type sequence and other things
6287 			pe.ParentGroup = pg;
6288 
6289 			PwGroup pgT = EntryTemplates.GetTemplatesGroup(pd);
6290 			Debug.Assert(pe.ParentGroup != null);
6291 			if((pgT == null) || !pe.IsContainedIn(pgT)) // No auto. for new template
6292 			{
6293 				if(pe.Strings.GetSafe(PwDefs.UserNameField).IsEmpty)
6294 					pe.Strings.Set(PwDefs.UserNameField, new ProtectedString(
6295 						pd.MemoryProtection.ProtectUserName, pd.DefaultUserName));
6296 
6297 				if(pe.Strings.GetSafe(PwDefs.PasswordField).IsEmpty)
6298 					PwGeneratorUtil.GenerateAuto(pe, pd);
6299 
6300 				if(!pe.Expires)
6301 				{
6302 					int nExpireDays = Program.Config.Defaults.NewEntryExpiresInDays;
6303 					if(nExpireDays >= 0)
6304 					{
6305 						pe.Expires = true;
6306 						pe.ExpiryTime = DateTime.UtcNow.AddDays(nExpireDays);
6307 					}
6308 				}
6309 			}
6310 
6311 			if(fAddPre != null) fAddPre(pe);
6312 
6313 			bool bSelectFullTitle = !pe.Strings.GetSafe(PwDefs.TitleField).IsEmpty;
6314 
6315 			PwEntryForm pForm = new PwEntryForm();
6316 			pForm.InitEx(pe, PwEditMode.AddNewEntry, pd, m_ilCurrentIcons,
6317 				false, bSelectFullTitle);
6318 			if(UIUtil.ShowDialogAndDestroy(pForm) == DialogResult.OK)
6319 			{
6320 				pg.AddEntry(pe, true);
6321 				UpdateUI(false, null, pd.UINeedsIconUpdate, null, true,
6322 					null, true, m_lvEntries);
6323 
6324 				PwObjectList<PwEntry> lSelect = new PwObjectList<PwEntry>();
6325 				lSelect.Add(pe);
6326 				SelectEntries(lSelect, true, true);
6327 				EnsureVisibleSelected(false);
6328 				UpdateUIState(false);
6329 
6330 				if(Program.Config.Application.AutoSaveAfterEntryEdit)
6331 					SaveDatabase(pd, sender);
6332 
6333 				if(fAddPost != null) fAddPost(pe);
6334 			}
6335 			else UpdateUI(false, null, pd.UINeedsIconUpdate, null,
6336 				pd.UINeedsIconUpdate, null, false);
6337 		}
6338 
6339 #if DEBUG
ConstructDebugMenu()6340 		private void ConstructDebugMenu()
6341 		{
6342 			ToolStripMenuItem tsmiDebug = new ToolStripMenuItem("Debug");
6343 			m_menuTools.DropDownItems.Insert(m_menuTools.DropDownItems.IndexOf(
6344 				m_menuToolsAdv) + 1, tsmiDebug);
6345 
6346 			ToolStripMenuItem tsmi = new ToolStripMenuItem("Set Database Custom Data Items (KDBX 4.1)");
6347 			tsmiDebug.DropDownItems.Add(tsmi);
6348 			tsmi.Click += delegate(object sender, EventArgs e)
6349 			{
6350 				PwDatabase pd = m_docMgr.ActiveDatabase;
6351 				if((pd == null) || !pd.IsOpen) return;
6352 
6353 				Random r = Program.GlobalRandom;
6354 				byte[] pb = new byte[12];
6355 				Action<string> f = delegate(string str)
6356 				{
6357 					r.NextBytes(pb);
6358 					pd.CustomData.Set("Test_" + str, Convert.ToBase64String(pb),
6359 						DateTime.UtcNow.AddSeconds(-(r.Next() % (60 * 60 * 24 * 7))));
6360 				};
6361 
6362 				for(char ch = 'A'; ch < 'E'; ++ch) f(ch.ToString());
6363 
6364 				UpdateUIState(true);
6365 			};
6366 		}
6367 #endif
6368 
GetPreviousParentGroupSafeI(IStructureItem it, PwGroup pgRoot)6369 		private static PwGroup GetPreviousParentGroupSafeI(IStructureItem it,
6370 			PwGroup pgRoot)
6371 		{
6372 			if(it == null) { Debug.Assert(false); return null; }
6373 
6374 			PwGroup pgParent = it.ParentGroup;
6375 			if(pgParent == null) return null; // Cannot move root group
6376 
6377 			PwUuid puPrev = it.PreviousParentGroup;
6378 			if(puPrev.Equals(PwUuid.Zero)) return null;
6379 
6380 			PwGroup pgPrev = pgRoot.FindGroup(puPrev, true);
6381 			if(pgPrev == null) return null;
6382 			if(pgPrev == pgParent) return null;
6383 
6384 			return pgPrev;
6385 		}
6386 
GetPreviousParentGroupSafe(PwGroup pg, PwGroup pgRoot)6387 		private static PwGroup GetPreviousParentGroupSafe(PwGroup pg, PwGroup pgRoot)
6388 		{
6389 			PwGroup pgPrev = GetPreviousParentGroupSafeI(pg, pgRoot);
6390 			if(pgPrev == null) return null;
6391 			if(pgPrev == pg) { Debug.Assert(false); return null; } // Cannot move into itself
6392 			if(pgPrev.IsContainedIn(pg)) return null;
6393 			if(!pgPrev.CanAddGroup(pg)) return null;
6394 
6395 			return pgPrev;
6396 		}
6397 
GetPreviousParentGroupSafe(PwEntry pe, PwGroup pgRoot)6398 		private static PwGroup GetPreviousParentGroupSafe(PwEntry pe, PwGroup pgRoot)
6399 		{
6400 			return GetPreviousParentGroupSafeI(pe, pgRoot);
6401 		}
6402 
GetPreviousParentGroupCmdInfo(PwGroup pg, PwEntry[] v, out string strCommand, out bool bEnabled, out bool bRecycleAtLeast1)6403 		private void GetPreviousParentGroupCmdInfo(PwGroup pg, PwEntry[] v,
6404 			out string strCommand, out bool bEnabled, out bool bRecycleAtLeast1)
6405 		{
6406 			strCommand = KPRes.MoveToPreviousParentGroup;
6407 			bEnabled = false;
6408 			bRecycleAtLeast1 = false;
6409 
6410 			PwDatabase pd = m_docMgr.ActiveDatabase;
6411 			if((pd == null) || !pd.IsOpen) return;
6412 
6413 			PwGroup pgRoot = pd.RootGroup;
6414 
6415 			bool bRestore = true, bRecycle = false;
6416 			PwGroup pgRecBin = null;
6417 			if(pd.RecycleBinEnabled)
6418 				pgRecBin = pgRoot.FindGroup(pd.RecycleBinUuid, true);
6419 
6420 			GFunc<IStructureItem, PwGroup, bool> fCheck = delegate(
6421 				IStructureItem it, PwGroup pgPrev)
6422 			{
6423 				if(it == null) { Debug.Assert(false); return false; }
6424 				if(pgPrev == null) return false;
6425 				Debug.Assert(pgPrev.Uuid.Equals(it.PreviousParentGroup));
6426 
6427 				PwGroup pgParent = it.ParentGroup;
6428 				if(pgParent == null) { Debug.Assert(false); return false; }
6429 
6430 				bool bInRecNow = ((pgRecBin != null) ? ((pgParent == pgRecBin) ||
6431 					pgParent.IsContainedIn(pgRecBin)) : false);
6432 				bool bInRecThen = ((pgRecBin != null) ? ((pgPrev == pgRecBin) ||
6433 					pgPrev.IsContainedIn(pgRecBin)) : false);
6434 
6435 				bRestore &= (bInRecNow && !bInRecThen);
6436 				bRecycle |= bInRecThen; // Move into/within
6437 
6438 				return true;
6439 			};
6440 
6441 			if(pg != null)
6442 			{
6443 				PwGroup pgPrev = GetPreviousParentGroupSafe(pg, pgRoot);
6444 				if(!fCheck(pg, pgPrev)) return;
6445 			}
6446 			else if((v != null) && (v.Length != 0)) // Empty => disable
6447 			{
6448 				foreach(PwEntry pe in v)
6449 				{
6450 					PwGroup pgPrev = GetPreviousParentGroupSafe(pe, pgRoot);
6451 					if(!fCheck(pe, pgPrev)) return;
6452 				}
6453 			}
6454 			else return;
6455 
6456 			if(bRestore) strCommand += " (" + KPRes.Restore + ")";
6457 			bEnabled = true;
6458 			bRecycleAtLeast1 = bRecycle;
6459 		}
6460 
UpdateMoveToPreviousParentGroupUI(PwGroup pg, PwEntry[] v, ToolStripMenuItem tsmi)6461 		private void UpdateMoveToPreviousParentGroupUI(PwGroup pg, PwEntry[] v,
6462 			ToolStripMenuItem tsmi)
6463 		{
6464 			if(tsmi == null) { Debug.Assert(false); return; }
6465 
6466 			string strCommand;
6467 			bool bEnabled, bRecycle;
6468 			GetPreviousParentGroupCmdInfo(pg, v, out strCommand, out bEnabled,
6469 				out bRecycle);
6470 
6471 			tsmi.Text = strCommand;
6472 			tsmi.Enabled = bEnabled;
6473 		}
6474 
ConfirmMoveToPreviousParentGroup(PwGroup pg, PwEntry[] v)6475 		private bool ConfirmMoveToPreviousParentGroup(PwGroup pg, PwEntry[] v)
6476 		{
6477 			string strCommand;
6478 			bool bEnabled, bRecycle;
6479 			GetPreviousParentGroupCmdInfo(pg, v, out strCommand, out bEnabled,
6480 				out bRecycle);
6481 
6482 			if(!bEnabled) { Debug.Assert(false); return false; }
6483 			if(bRecycle)
6484 			{
6485 				string strMsg = KPRes.RecycleMoveInfo + MessageService.NewParagraph +
6486 					KPRes.AskContinue;
6487 				if(!MessageService.AskYesNo(strMsg, PwDefs.ShortProductName, false))
6488 					return false;
6489 			}
6490 
6491 			return true;
6492 		}
6493 
MoveToPreviousParentGroup(bool bEntry)6494 		private void MoveToPreviousParentGroup(bool bEntry)
6495 		{
6496 			PwDatabase pd = m_docMgr.ActiveDatabase;
6497 			if((pd == null) || !pd.IsOpen) { Debug.Assert(false); return; }
6498 
6499 			PwGroup pgRoot = pd.RootGroup;
6500 
6501 			if(bEntry)
6502 			{
6503 				PwEntry[] v = GetSelectedEntries();
6504 				if((v == null) || (v.Length == 0)) { Debug.Assert(false); return; }
6505 
6506 				if(!ConfirmMoveToPreviousParentGroup(null, v)) return;
6507 
6508 				foreach(PwEntry pe in v)
6509 				{
6510 					PwGroup pgPrev = GetPreviousParentGroupSafe(pe, pgRoot);
6511 					if(pgPrev == null) { Debug.Assert(false); continue; }
6512 
6513 					PwGroup pgParent = pe.ParentGroup;
6514 					if(!pgParent.Entries.Remove(pe)) { Debug.Assert(false); continue; }
6515 
6516 					pgPrev.AddEntry(pe, true, true);
6517 					pe.PreviousParentGroup = pgParent.Uuid;
6518 				}
6519 			}
6520 			else // Group
6521 			{
6522 				PwGroup pg = GetSelectedGroup();
6523 
6524 				if(!ConfirmMoveToPreviousParentGroup(pg, null)) return;
6525 
6526 				PwGroup pgPrev = GetPreviousParentGroupSafe(pg, pgRoot);
6527 				if(pgPrev == null) { Debug.Assert(false); return; }
6528 
6529 				PwGroup pgParent = pg.ParentGroup;
6530 				if(!pgParent.Groups.Remove(pg)) { Debug.Assert(false); return; }
6531 
6532 				pgPrev.AddGroup(pg, true, true);
6533 				pg.PreviousParentGroup = pgParent.Uuid;
6534 			}
6535 
6536 			UpdateUI(false, null, !bEntry, null, true, null, true);
6537 		}
6538 	}
6539 }
6540