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.ComponentModel;
23 using System.Diagnostics;
24 using System.Drawing;
25 using System.Text;
26 using System.Windows.Forms;
27 
28 using KeePass.App;
29 using KeePass.App.Configuration;
30 using KeePass.Resources;
31 using KeePass.UI;
32 using KeePass.Util;
33 
34 using KeePassLib;
35 using KeePassLib.Collections;
36 using KeePassLib.Interfaces;
37 using KeePassLib.Utility;
38 
39 namespace KeePass.Forms
40 {
41 	public partial class SearchForm : Form
42 	{
43 		private readonly string ProfileCustom = "(" + KPRes.Custom + ")";
44 
45 		private PwDatabase m_pdContext = null;
46 		private PwGroup m_pgRoot = null;
47 
48 		private uint m_uBlockProfileAuto = 0;
49 
50 		private PwGroup m_pgResults = null;
51 		public PwGroup SearchResultsGroup
52 		{
53 			get { return m_pgResults; }
54 		}
55 
56 		private SearchParameters m_spResult = null;
57 		internal SearchParameters SearchResultParameters
58 		{
59 			get { return m_spResult; }
60 		}
61 
62 		private string m_strInitProfile = string.Empty;
63 		[Browsable(false)]
64 		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
65 		[DefaultValue("")]
66 		internal string InitProfile
67 		{
68 			get { return m_strInitProfile; }
69 			set { m_strInitProfile = value; }
70 		}
71 
SearchForm()72 		public SearchForm()
73 		{
74 			InitializeComponent();
75 			GlobalWindowManager.InitializeForm(this);
76 		}
77 
78 		/// <summary>
79 		/// Initialize the form. Must be called before the dialog is displayed.
80 		/// </summary>
InitEx(PwDatabase pdContext, PwGroup pgRoot)81 		public void InitEx(PwDatabase pdContext, PwGroup pgRoot)
82 		{
83 			m_pdContext = pdContext;
84 			m_pgRoot = pgRoot;
85 		}
86 
OnFormLoad(object sender, EventArgs e)87 		private void OnFormLoad(object sender, EventArgs e)
88 		{
89 			if(m_pgRoot == null) { Debug.Assert(false); throw new InvalidOperationException(); }
90 
91 			GlobalWindowManager.AddWindow(this);
92 
93 			string strTitle = KPRes.SearchTitle;
94 			string strDesc = KPRes.SearchDesc2;
95 			if((m_pgRoot.ParentGroup != null) && !string.IsNullOrEmpty(m_pgRoot.Name))
96 			{
97 				strTitle += " (" + KPRes.SelectedGroup + ")";
98 				strDesc = KPRes.Group + ": '" + m_pgRoot.Name + "'.";
99 			}
100 
101 			BannerFactory.CreateBannerEx(this, m_bannerImage,
102 				Properties.Resources.B48x48_XMag, strTitle, strDesc);
103 			this.Icon = AppIcons.Default;
104 			this.Text = KPRes.SearchTitle;
105 
106 			UIUtil.SetButtonImage(m_btnProfileAdd,
107 				Properties.Resources.B16x16_FileSaveAs, false);
108 			UIUtil.SetButtonImage(m_btnProfileDelete,
109 				Properties.Resources.B16x16_EditDelete, true);
110 
111 			UIUtil.SetText(m_cbDerefData, m_cbDerefData.Text + " (" + KPRes.Slow + ")");
112 
113 			UIUtil.ConfigureToolTip(m_ttMain);
114 			UIUtil.SetToolTip(m_ttMain, m_btnProfileAdd, KPRes.ProfileSaveDesc, false);
115 			UIUtil.SetToolTip(m_ttMain, m_btnProfileDelete, KPRes.ProfileDeleteDesc, false);
116 
117 			UIUtil.AccSetName(m_btnProfileAdd, KPRes.ProfileSave);
118 			UIUtil.AccSetName(m_btnProfileDelete, KPRes.ProfileDelete);
119 
120 			SearchParameters sp = (!string.IsNullOrEmpty(m_strInitProfile) ?
121 				Program.Config.Search.FindProfile(m_strInitProfile) : null);
122 			Debug.Assert(string.IsNullOrEmpty(m_strInitProfile) || (sp != null));
123 			UpdateProfilesList((sp != null) ? sp.Name : ProfileCustom);
124 			SetSearchParameters(sp ?? Program.Config.Search.LastUsedProfile);
125 
126 			m_tbSearch.TextChanged += this.OnProfilePropertyChanged;
127 			m_rbModeSimple.CheckedChanged += this.OnProfilePropertyChanged;
128 			m_rbModeRegular.CheckedChanged += this.OnProfilePropertyChanged;
129 			m_rbModeXPath.CheckedChanged += this.OnProfilePropertyChanged;
130 
131 			CheckBox[] v = new CheckBox[] {
132 				m_cbTitle, m_cbUserName, m_cbPassword, m_cbUrl, m_cbNotes,
133 				m_cbStringsOther, m_cbStringName, m_cbTags, m_cbUuid,
134 				m_cbGroupPath, m_cbGroupName, m_cbHistory,
135 				m_cbCaseSensitive, m_cbExcludeExpired, m_cbIgnoreGroupSettings,
136 				m_cbDerefData
137 			};
138 			foreach(CheckBox cb in v) cb.CheckedChanged += this.OnProfilePropertyChanged;
139 
140 			UpdateUIState();
141 			m_tbSearch.SelectAll();
142 		}
143 
OnFormClosed(object sender, FormClosedEventArgs e)144 		private void OnFormClosed(object sender, FormClosedEventArgs e)
145 		{
146 			Program.Config.Search.LastUsedProfile = GetSearchParameters();
147 
148 			GlobalWindowManager.RemoveWindow(this);
149 		}
150 
OnBtnOK(object sender, EventArgs e)151 		private void OnBtnOK(object sender, EventArgs e)
152 		{
153 			SearchParameters sp = GetSearchParameters();
154 
155 			Form fOptDialog;
156 			IStatusLogger sl = StatusUtil.CreateStatusDialog(this, out fOptDialog,
157 				null, KPRes.SearchingOp + "...", true, false);
158 			// if(fOptDialog != null) Program.MainForm.RedirectActivationPush(fOptDialog);
159 			this.Enabled = false;
160 
161 			PwGroup pgResults = null;
162 			Exception exFind = null;
163 			try { pgResults = SearchUtil.Find(sp, m_pgRoot, sl); }
164 			catch(Exception ex) { exFind = ex; }
165 
166 			this.Enabled = true;
167 			// if(fOptDialog != null) Program.MainForm.RedirectActivationPop();
168 			sl.EndLogging();
169 
170 			if(exFind != null)
171 			{
172 				MessageService.ShowWarning(sp.SearchString, exFind);
173 				this.DialogResult = DialogResult.None;
174 			}
175 			else if(pgResults != null)
176 			{
177 				m_pgResults = pgResults;
178 				m_spResult = sp;
179 			}
180 			else
181 			{
182 				Debug.Assert(false);
183 				this.DialogResult = DialogResult.None;
184 			}
185 		}
186 
UpdateProfilesList(string strSelect)187 		private void UpdateProfilesList(string strSelect)
188 		{
189 			++m_uBlockProfileAuto;
190 
191 			List<object> l = new List<object>();
192 			int iSel = 0;
193 
194 			l.Add(ProfileCustom);
195 
196 			Program.Config.Search.SortProfiles();
197 			foreach(SearchParameters sp in Program.Config.Search.UserProfiles)
198 			{
199 				string strName = sp.Name;
200 				if(strName == strSelect) iSel = l.Count;
201 				l.Add(strName);
202 			}
203 
204 			m_cmbProfiles.Items.Clear();
205 			m_cmbProfiles.Items.AddRange(l.ToArray());
206 
207 			Debug.Assert((strSelect == ProfileCustom) || (iSel != 0));
208 			m_cmbProfiles.SelectedIndex = iSel;
209 
210 			--m_uBlockProfileAuto;
211 		}
212 
SetSearchParameters(SearchParameters sp)213 		private void SetSearchParameters(SearchParameters sp)
214 		{
215 			if(sp == null) { Debug.Assert(false); sp = new SearchParameters(); }
216 
217 			++m_uBlockProfileAuto;
218 
219 			Debug.Assert((m_cmbProfiles.Text == ProfileCustom) ||
220 				(m_cmbProfiles.Text == sp.Name));
221 
222 			m_tbSearch.Text = sp.SearchString;
223 
224 			if(sp.SearchMode == PwSearchMode.Regular)
225 				m_rbModeRegular.Checked = true;
226 			else if(sp.SearchMode == PwSearchMode.XPath)
227 				m_rbModeXPath.Checked = true;
228 			else
229 			{
230 				Debug.Assert(sp.SearchMode == PwSearchMode.Simple);
231 				m_rbModeSimple.Checked = true;
232 			}
233 
234 			m_cbTitle.Checked = sp.SearchInTitles;
235 			m_cbUserName.Checked = sp.SearchInUserNames;
236 			m_cbPassword.Checked = sp.SearchInPasswords;
237 			m_cbUrl.Checked = sp.SearchInUrls;
238 			m_cbNotes.Checked = sp.SearchInNotes;
239 			m_cbStringsOther.Checked = sp.SearchInOther;
240 			m_cbStringName.Checked = sp.SearchInStringNames;
241 			m_cbTags.Checked = sp.SearchInTags;
242 			m_cbUuid.Checked = sp.SearchInUuids;
243 			m_cbGroupPath.Checked = sp.SearchInGroupPaths;
244 			m_cbGroupName.Checked = sp.SearchInGroupNames;
245 			m_cbHistory.Checked = sp.SearchInHistory;
246 
247 			StringComparison sc = sp.ComparisonMode;
248 			m_cbCaseSensitive.Checked = ((sc != StringComparison.CurrentCultureIgnoreCase) &&
249 				(sc != StringComparison.InvariantCultureIgnoreCase) &&
250 				(sc != StringComparison.OrdinalIgnoreCase));
251 
252 			m_cbExcludeExpired.Checked = sp.ExcludeExpired;
253 			m_cbIgnoreGroupSettings.Checked = !sp.RespectEntrySearchingDisabled;
254 
255 			string strTrf = SearchUtil.GetTransformation(sp);
256 			m_cbDerefData.Checked = (strTrf == SearchUtil.StrTrfDeref);
257 
258 			--m_uBlockProfileAuto;
259 		}
260 
GetSearchParameters()261 		private SearchParameters GetSearchParameters()
262 		{
263 			SearchParameters sp = new SearchParameters();
264 
265 			sp.Name = m_cmbProfiles.Text;
266 			sp.SearchString = m_tbSearch.Text;
267 
268 			if(m_rbModeRegular.Checked)
269 				sp.SearchMode = PwSearchMode.Regular;
270 			else if(m_rbModeXPath.Checked)
271 				sp.SearchMode = PwSearchMode.XPath;
272 			else
273 			{
274 				Debug.Assert(m_rbModeSimple.Checked);
275 				sp.SearchMode = PwSearchMode.Simple;
276 			}
277 
278 			sp.SearchInTitles = m_cbTitle.Checked;
279 			sp.SearchInUserNames = m_cbUserName.Checked;
280 			sp.SearchInPasswords = m_cbPassword.Checked;
281 			sp.SearchInUrls = m_cbUrl.Checked;
282 			sp.SearchInNotes = m_cbNotes.Checked;
283 			sp.SearchInOther = m_cbStringsOther.Checked;
284 			sp.SearchInStringNames = m_cbStringName.Checked;
285 			sp.SearchInTags = m_cbTags.Checked;
286 			sp.SearchInUuids = m_cbUuid.Checked;
287 			sp.SearchInGroupPaths = m_cbGroupPath.Checked;
288 			sp.SearchInGroupNames = m_cbGroupName.Checked;
289 			sp.SearchInHistory = m_cbHistory.Checked;
290 
291 			sp.ComparisonMode = (m_cbCaseSensitive.Checked ?
292 				StringComparison.InvariantCulture :
293 				StringComparison.InvariantCultureIgnoreCase);
294 
295 			sp.ExcludeExpired = m_cbExcludeExpired.Checked;
296 			sp.RespectEntrySearchingDisabled = !m_cbIgnoreGroupSettings.Checked;
297 
298 			SearchUtil.SetTransformation(sp, (m_cbDerefData.Checked ?
299 				SearchUtil.StrTrfDeref : string.Empty));
300 
301 			return sp;
302 		}
303 
UpdateUIState()304 		private void UpdateUIState()
305 		{
306 			++m_uBlockProfileAuto;
307 
308 			bool bCustom = (m_cmbProfiles.Text == ProfileCustom);
309 			bool bXPath = m_rbModeXPath.Checked;
310 
311 			m_btnProfileDelete.Enabled = !bCustom;
312 
313 			m_cbStringName.Enabled = !bXPath;
314 			if(bXPath) m_cbStringName.Checked = true;
315 
316 			m_cbUuid.Enabled = !bXPath;
317 			if(bXPath) m_cbUuid.Checked = true;
318 
319 			m_cbGroupPath.Enabled = !bXPath;
320 			if(bXPath) m_cbGroupPath.Checked = false;
321 
322 			bool bGroupPath = m_cbGroupPath.Checked;
323 			m_cbGroupName.Enabled = !bGroupPath;
324 			if(bGroupPath) m_cbGroupName.Checked = true;
325 
326 			m_cbCaseSensitive.Enabled = !bXPath;
327 			if(bXPath) m_cbCaseSensitive.Checked = true;
328 
329 			m_cbDerefData.Enabled = !bXPath;
330 			if(bXPath) m_cbDerefData.Checked = false;
331 
332 			--m_uBlockProfileAuto;
333 		}
334 
OnProfilesSelectedIndexChanged(object sender, EventArgs e)335 		private void OnProfilesSelectedIndexChanged(object sender, EventArgs e)
336 		{
337 			if(m_uBlockProfileAuto != 0) return;
338 
339 			string strName = m_cmbProfiles.Text;
340 			if(strName != ProfileCustom)
341 				SetSearchParameters(Program.Config.Search.FindProfile(strName));
342 
343 			UpdateUIState();
344 		}
345 
OnProfilePropertyChanged(object sender, EventArgs e)346 		private void OnProfilePropertyChanged(object sender, EventArgs e)
347 		{
348 			if(m_uBlockProfileAuto != 0) return;
349 
350 			Debug.Assert((m_cmbProfiles.Items.Count != 0) && !m_cmbProfiles.Sorted &&
351 				((m_cmbProfiles.Items[0] as string) == ProfileCustom));
352 			if(m_cmbProfiles.Items.Count != 0)
353 				m_cmbProfiles.SelectedIndex = 0;
354 
355 			UpdateUIState();
356 		}
357 
OnBtnProfileAdd(object sender, EventArgs e)358 		private void OnBtnProfileAdd(object sender, EventArgs e)
359 		{
360 			List<string> lNames = new List<string>();
361 			foreach(SearchParameters sp in Program.Config.Search.UserProfiles)
362 				lNames.Add(sp.Name);
363 
364 			SingleLineEditForm dlg = new SingleLineEditForm();
365 			dlg.InitEx(KPRes.ProfileSave, KPRes.ProfileSaveDesc,
366 				KPRes.ProfileSavePrompt, Properties.Resources.B48x48_KMag,
367 				string.Empty, lNames.ToArray());
368 
369 			if(dlg.ShowDialog() == DialogResult.OK)
370 			{
371 				string strName = dlg.ResultString;
372 
373 				if(string.IsNullOrEmpty(strName) || (strName == ProfileCustom))
374 					MessageService.ShowWarning(KPRes.FieldNameInvalid);
375 				else
376 				{
377 					SearchParameters sp = GetSearchParameters();
378 					sp.Name = strName;
379 
380 					AceSearch aceSearch = Program.Config.Search;
381 					int i = aceSearch.FindProfileIndex(strName);
382 					if(i >= 0) aceSearch.UserProfiles[i] = sp;
383 					else aceSearch.UserProfiles.Add(sp);
384 
385 					UpdateProfilesList(strName);
386 					UpdateUIState();
387 				}
388 			}
389 			UIUtil.DestroyForm(dlg);
390 		}
391 
OnBtnProfileDelete(object sender, EventArgs e)392 		private void OnBtnProfileDelete(object sender, EventArgs e)
393 		{
394 			string strName = m_cmbProfiles.Text;
395 			if(strName == ProfileCustom) { Debug.Assert(false); return; }
396 
397 			AceSearch aceSearch = Program.Config.Search;
398 			int i = aceSearch.FindProfileIndex(strName);
399 			if(i >= 0)
400 			{
401 				aceSearch.UserProfiles.RemoveAt(i);
402 
403 				UpdateProfilesList(ProfileCustom);
404 				UpdateUIState();
405 			}
406 			else { Debug.Assert(false); }
407 		}
408 
OnBtnHelp(object sender, EventArgs e)409 		private void OnBtnHelp(object sender, EventArgs e)
410 		{
411 			AppHelp.ShowHelp(AppDefs.HelpTopics.Search, null);
412 		}
413 	}
414 }
415