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.Text;
25 using System.Text.RegularExpressions;
26 using System.Windows.Forms;
27 
28 using KeePass.Ecas;
29 using KeePass.Resources;
30 using KeePass.UI;
31 using KeePass.Util.Spr;
32 
33 using KeePassLib;
34 using KeePassLib.Utility;
35 
36 namespace KeePass.Ecas
37 {
38 	public enum EcasTypeDxMode // Type data exchange modes
39 	{
40 		None = 0,
41 		Selection, // DX with the type UI control (combobox)
42 		ParamsTag // Get type from the parameters control
43 	}
44 
45 	public static class EcasUtil
46 	{
47 		public static readonly uint StdCompareEqual = 0;
48 		public static readonly uint StdCompareNotEqual = 1;
49 		public static readonly uint StdCompareLesser = 2;
50 		public static readonly uint StdCompareLesserEqual = 3;
51 		public static readonly uint StdCompareGreater = 4;
52 		public static readonly uint StdCompareGreaterEqual = 5;
53 
54 		private static EcasEnum m_enumCompare = null;
55 		public static EcasEnum StdCompare
56 		{
57 			get
58 			{
59 				if(m_enumCompare == null)
60 					m_enumCompare = new EcasEnum(new EcasEnumItem[] {
61 						new EcasEnumItem(StdCompareEqual, "="),
62 						new EcasEnumItem(StdCompareNotEqual, "<>"),
63 						new EcasEnumItem(StdCompareLesser, "<"),
64 						new EcasEnumItem(StdCompareLesserEqual, "<="),
65 						new EcasEnumItem(StdCompareGreater, ">"),
66 						new EcasEnumItem(StdCompareGreaterEqual, ">=") });
67 
68 				return m_enumCompare;
69 			}
70 		}
71 
72 		public static readonly uint StdStringCompareEquals = 0;
73 		public static readonly uint StdStringCompareContains = 1;
74 		public static readonly uint StdStringCompareStartsWith = 2;
75 		public static readonly uint StdStringCompareEndsWith = 3;
76 		public static readonly uint StdStringCompareRegEx = 4;
77 
78 		private static EcasEnum m_enumStringCompare = null;
79 		public static EcasEnum StdStringCompare
80 		{
81 			get
82 			{
83 				if(m_enumStringCompare == null)
84 					m_enumStringCompare = new EcasEnum(new EcasEnumItem[] {
85 						new EcasEnumItem(StdStringCompareEquals, KPRes.EqualsOp),
86 						new EcasEnumItem(StdStringCompareContains, KPRes.ContainsOp),
87 						new EcasEnumItem(StdStringCompareStartsWith, KPRes.StartsWith),
88 						new EcasEnumItem(StdStringCompareEndsWith, KPRes.EndsWith),
89 						new EcasEnumItem(StdStringCompareRegEx, KPRes.MatchesRegEx) });
90 
91 				return m_enumStringCompare;
92 			}
93 		}
94 
GetParamString(List<string> vParams, int iIndex)95 		public static string GetParamString(List<string> vParams, int iIndex)
96 		{
97 			return GetParamString(vParams, iIndex, string.Empty);
98 		}
99 
GetParamString(List<string> vParams, int iIndex, bool bSprCompile)100 		public static string GetParamString(List<string> vParams, int iIndex,
101 			bool bSprCompile)
102 		{
103 			return GetParamString(vParams, iIndex, bSprCompile, false);
104 		}
105 
GetParamString(List<string> vParams, int iIndex, bool bSprCompile, bool bSprForCommandLine)106 		public static string GetParamString(List<string> vParams, int iIndex,
107 			bool bSprCompile, bool bSprForCommandLine)
108 		{
109 			string str = GetParamString(vParams, iIndex, string.Empty);
110 
111 			if(bSprCompile && !string.IsNullOrEmpty(str))
112 			{
113 				PwEntry pe = null;
114 				try { pe = Program.MainForm.GetSelectedEntry(false); }
115 				catch(Exception) { Debug.Assert(false); }
116 
117 				PwDatabase pd = Program.MainForm.DocumentManager.SafeFindContainerOf(pe);
118 
119 				// The trigger system does not update the UI itself,
120 				// thus ignore state-changing placeholders
121 				str = SprEngine.Compile(str, new SprContext(pe, pd,
122 					(SprCompileFlags.All & ~SprCompileFlags.StateChanging),
123 					false, bSprForCommandLine));
124 			}
125 
126 			return str;
127 		}
128 
GetParamString(List<string> vParams, int iIndex, string strDefault)129 		public static string GetParamString(List<string> vParams, int iIndex,
130 			string strDefault)
131 		{
132 			if(vParams == null) { Debug.Assert(false); return strDefault; }
133 			if(iIndex < 0) { Debug.Assert(false); return strDefault; }
134 			if(iIndex >= vParams.Count) return strDefault; // No assert
135 
136 			return vParams[iIndex];
137 		}
138 
GetParamBool(List<string> vParams, int iIndex)139 		public static bool GetParamBool(List<string> vParams, int iIndex)
140 		{
141 			string str = GetParamString(vParams, iIndex, string.Empty);
142 			return StrUtil.StringToBool(str);
143 		}
144 
GetParamUInt(List<string> vParams, int iIndex)145 		public static uint GetParamUInt(List<string> vParams, int iIndex)
146 		{
147 			return GetParamUInt(vParams, iIndex, 0);
148 		}
149 
GetParamUInt(List<string> vParams, int iIndex, uint uDefault)150 		public static uint GetParamUInt(List<string> vParams, int iIndex,
151 			uint uDefault)
152 		{
153 			string str = GetParamString(vParams, iIndex, string.Empty);
154 			uint u;
155 			if(uint.TryParse(str, out u)) return u;
156 			return uDefault;
157 		}
158 
GetParamEnum(List<string> vParams, int iIndex, uint uDefault, EcasEnum enumItems)159 		public static uint GetParamEnum(List<string> vParams, int iIndex,
160 			uint uDefault, EcasEnum enumItems)
161 		{
162 			if(enumItems == null) { Debug.Assert(false); return uDefault; }
163 
164 			string str = GetParamString(vParams, iIndex, null);
165 			if(string.IsNullOrEmpty(str)) { Debug.Assert(false); return uDefault; }
166 
167 			uint uID;
168 			if(!uint.TryParse(str, out uID)) { Debug.Assert(false); return uDefault; }
169 
170 			// Make sure the enumeration contains the value
171 			if(enumItems.GetItemString(uID, null) == null) { Debug.Assert(false); return uDefault; }
172 
173 			return uID;
174 		}
175 
ParametersToDataGridView(DataGridView dg, IEcasParameterized p, IEcasObject objDefaults)176 		public static void ParametersToDataGridView(DataGridView dg,
177 			IEcasParameterized p, IEcasObject objDefaults)
178 		{
179 			if(dg == null) throw new ArgumentNullException("dg");
180 			if(p == null) throw new ArgumentNullException("p");
181 			if(p.Parameters == null) throw new ArgumentException();
182 			if(objDefaults == null) throw new ArgumentNullException("objDefaults");
183 			if(objDefaults.Parameters == null) throw new ArgumentException();
184 
185 			dg.Rows.Clear();
186 			dg.Columns.Clear();
187 
188 			Color clrFG = dg.DefaultCellStyle.ForeColor;
189 			Color clrBG = dg.DefaultCellStyle.BackColor;
190 
191 			// https://sourceforge.net/p/keepass/bugs/1808/
192 			if(UIUtil.IsDarkColor(clrFG) == UIUtil.IsDarkColor(clrBG))
193 				clrFG = (UIUtil.IsDarkColor(clrBG) ? Color.White : Color.Black);
194 
195 			Color clrValueBG = clrBG;
196 			if(UIUtil.IsDarkColor(clrBG))
197 				clrValueBG = UIUtil.LightenColor(clrValueBG, 0.075);
198 			else clrValueBG = UIUtil.DarkenColor(clrValueBG, 0.075);
199 
200 			dg.ColumnHeadersVisible = false;
201 			dg.RowHeadersVisible = false;
202 			dg.GridColor = clrBG;
203 			dg.BackgroundColor = clrBG;
204 			dg.DefaultCellStyle.ForeColor = clrFG;
205 			dg.DefaultCellStyle.BackColor = clrBG;
206 			dg.DefaultCellStyle.SelectionForeColor = clrFG;
207 			dg.DefaultCellStyle.SelectionBackColor = clrBG;
208 			dg.AllowDrop = false;
209 			dg.AllowUserToAddRows = false;
210 			dg.AllowUserToDeleteRows = false;
211 			dg.AllowUserToOrderColumns = false;
212 			dg.AllowUserToResizeColumns = false;
213 			dg.AllowUserToResizeRows = false;
214 			// dg.EditMode: see below
215 			dg.Tag = p;
216 
217 			int nWidth = (dg.ClientSize.Width - UIUtil.GetVScrollBarWidth());
218 			dg.Columns.Add("Name", KPRes.FieldName);
219 			dg.Columns.Add("Value", KPRes.FieldValue);
220 			dg.Columns[0].Width = (nWidth / 2);
221 			dg.Columns[1].Width = (nWidth / 2);
222 
223 			bool bUseDefaults = true;
224 			if(objDefaults.Type == null) { Debug.Assert(false); } // Optimistic
225 			else if(p.Type == null) { Debug.Assert(false); } // Optimistic
226 			else if(!objDefaults.Type.Equals(p.Type)) bUseDefaults = false;
227 
228 			for(int i = 0; i < p.Parameters.Length; ++i)
229 			{
230 				EcasParameter ep = p.Parameters[i];
231 
232 				dg.Rows.Add();
233 				DataGridViewRow row = dg.Rows[dg.Rows.Count - 1];
234 				DataGridViewCellCollection cc = row.Cells;
235 
236 				Debug.Assert(cc.Count == 2);
237 				cc[0].Value = ep.Name;
238 				cc[0].ReadOnly = true;
239 
240 				string strParam = (bUseDefaults ? EcasUtil.GetParamString(
241 					objDefaults.Parameters, i) : string.Empty);
242 
243 				DataGridViewCell c = null;
244 				switch(ep.Type)
245 				{
246 					case EcasValueType.String:
247 						c = new DataGridViewTextBoxCell();
248 						c.Value = strParam;
249 						break;
250 
251 					case EcasValueType.Bool:
252 						c = new DataGridViewCheckBoxCell(false);
253 						c.Value = StrUtil.StringToBool(strParam);
254 						break;
255 
256 					case EcasValueType.EnumStrings:
257 						DataGridViewComboBoxCell cmb = new DataGridViewComboBoxCell();
258 						cmb.Sorted = false;
259 						cmb.DisplayStyle = DataGridViewComboBoxDisplayStyle.DropDownButton;
260 						int iFound = -1;
261 						for(int e = 0; e < ep.EnumValues.ItemCount; ++e)
262 						{
263 							EcasEnumItem eei = ep.EnumValues.Items[e];
264 							cmb.Items.Add(eei.Name);
265 							if(eei.ID.ToString() == strParam) iFound = e;
266 						}
267 						if(iFound >= 0) cmb.Value = ep.EnumValues.Items[iFound].Name;
268 						else if(ep.EnumValues.ItemCount > 0) cmb.Value = ep.EnumValues.Items[0].Name;
269 						else { Debug.Assert(false); }
270 						c = cmb;
271 						break;
272 
273 					case EcasValueType.Int64:
274 						c = new DataGridViewTextBoxCell();
275 						c.Value = FilterTypeI64(strParam);
276 						break;
277 
278 					case EcasValueType.UInt64:
279 						c = new DataGridViewTextBoxCell();
280 						c.Value = FilterTypeU64(strParam);
281 						break;
282 
283 					default:
284 						Debug.Assert(false);
285 						break;
286 				}
287 
288 				if(c != null)
289 				{
290 					cc[1] = c;
291 					cc[1].ReadOnly = false;
292 				}
293 				else cc[1].ReadOnly = true;
294 
295 				cc[1].Style.ForeColor = clrFG;
296 				cc[1].Style.BackColor = clrValueBG;
297 				cc[1].Style.SelectionForeColor = clrFG;
298 				cc[1].Style.SelectionBackColor = clrValueBG;
299 			}
300 
301 			// Perform postponed setting of EditMode (cannot set it earlier
302 			// due to a Mono bug on FreeBSD);
303 			// https://sourceforge.net/p/keepass/discussion/329220/thread/cb8270e2/
304 			dg.EditMode = DataGridViewEditMode.EditOnEnter;
305 		}
306 
DataGridViewToParameters(DataGridView dg, IEcasObject objOut, IEcasParameterized eTypeInfo)307 		public static void DataGridViewToParameters(DataGridView dg, IEcasObject objOut,
308 			IEcasParameterized eTypeInfo)
309 		{
310 			if(dg == null) throw new ArgumentNullException("dg");
311 			if(objOut == null) throw new ArgumentNullException("objOut");
312 			// if(vParamDesc == null) throw new ArgumentNullException("vParamDesc");
313 			// if(dg.Rows.Count != vParamDesc.Length) { Debug.Assert(false); return; }
314 
315 			objOut.Parameters.Clear();
316 
317 			bool bTypeInfoValid = ((eTypeInfo != null) && (eTypeInfo.Parameters.Length ==
318 				dg.Rows.Count));
319 
320 			for(int i = 0; i < dg.RowCount; ++i)
321 			{
322 				DataGridViewCell c = dg.Rows[i].Cells[1];
323 				object oValue = c.Value;
324 				string strValue = ((oValue != null) ? oValue.ToString() : string.Empty);
325 
326 				if(bTypeInfoValid && (eTypeInfo.Parameters[i].EnumValues != null) &&
327 					(c is DataGridViewComboBoxCell))
328 				{
329 					objOut.Parameters.Add(eTypeInfo.Parameters[i].EnumValues.GetItemID(
330 						strValue, 0).ToString());
331 				}
332 				else objOut.Parameters.Add(strValue);
333 			}
334 		}
335 
UpdateDialog(EcasObjectType objType, ComboBox cmbTypes, DataGridView dgvParams, IEcasObject o, bool bGuiToInternal, EcasTypeDxMode dxType)336 		public static bool UpdateDialog(EcasObjectType objType, ComboBox cmbTypes,
337 			DataGridView dgvParams, IEcasObject o, bool bGuiToInternal,
338 			EcasTypeDxMode dxType)
339 		{
340 			bool bResult = true;
341 
342 			try
343 			{
344 				if(bGuiToInternal)
345 				{
346 					IEcasParameterized eTypeInfo = null;
347 
348 					if(dxType == EcasTypeDxMode.Selection)
349 					{
350 						string strSel = (cmbTypes.SelectedItem as string);
351 						if(!string.IsNullOrEmpty(strSel))
352 						{
353 							if(objType == EcasObjectType.Event)
354 							{
355 								eTypeInfo = Program.EcasPool.FindEvent(strSel);
356 								o.Type = eTypeInfo.Type;
357 							}
358 							else if(objType == EcasObjectType.Condition)
359 							{
360 								eTypeInfo = Program.EcasPool.FindCondition(strSel);
361 								o.Type = eTypeInfo.Type;
362 							}
363 							else if(objType == EcasObjectType.Action)
364 							{
365 								eTypeInfo = Program.EcasPool.FindAction(strSel);
366 								o.Type = eTypeInfo.Type;
367 							}
368 							else { Debug.Assert(false); }
369 						}
370 					}
371 					else if(dxType == EcasTypeDxMode.ParamsTag)
372 					{
373 						IEcasParameterized p = (dgvParams.Tag as IEcasParameterized);
374 						if((p != null) && (p.Type != null))
375 						{
376 							eTypeInfo = p;
377 							o.Type = eTypeInfo.Type;
378 						}
379 						else { Debug.Assert(false); }
380 					}
381 
382 					EcasUtil.DataGridViewToParameters(dgvParams, o, eTypeInfo);
383 				}
384 				else // Internal to GUI
385 				{
386 					if(dxType == EcasTypeDxMode.Selection)
387 					{
388 						if(o.Type.Equals(PwUuid.Zero))
389 							cmbTypes.SelectedIndex = 0;
390 						else
391 						{
392 							int i = -1;
393 							if(objType == EcasObjectType.Event)
394 								i = cmbTypes.FindString(Program.EcasPool.FindEvent(o.Type).Name);
395 							else if(objType == EcasObjectType.Condition)
396 								i = cmbTypes.FindString(Program.EcasPool.FindCondition(o.Type).Name);
397 							else if(objType == EcasObjectType.Action)
398 								i = cmbTypes.FindString(Program.EcasPool.FindAction(o.Type).Name);
399 							else { Debug.Assert(false); }
400 
401 							if(i >= 0) cmbTypes.SelectedIndex = i;
402 							else { Debug.Assert(false); }
403 						}
404 					}
405 					else { Debug.Assert(dxType != EcasTypeDxMode.ParamsTag); }
406 
407 					IEcasParameterized t = null;
408 					if(objType == EcasObjectType.Event)
409 						t = Program.EcasPool.FindEvent(cmbTypes.SelectedItem as string);
410 					else if(objType == EcasObjectType.Condition)
411 						t = Program.EcasPool.FindCondition(cmbTypes.SelectedItem as string);
412 					else if(objType == EcasObjectType.Action)
413 						t = Program.EcasPool.FindAction(cmbTypes.SelectedItem as string);
414 					else { Debug.Assert(false); }
415 
416 					if(t != null) EcasUtil.ParametersToDataGridView(dgvParams, t, o);
417 				}
418 			}
419 			catch(Exception e) { MessageService.ShowWarning(e); bResult = false; }
420 
421 			return bResult;
422 		}
423 
ParametersToString(IEcasObject ecasObj, EcasParameter[] vParamInfo)424 		public static string ParametersToString(IEcasObject ecasObj,
425 			EcasParameter[] vParamInfo)
426 		{
427 			if(ecasObj == null) throw new ArgumentNullException("ecasObj");
428 			if(ecasObj.Parameters == null) throw new ArgumentException();
429 
430 			bool bParamInfoValid = true;
431 			if((vParamInfo == null) || (ecasObj.Parameters.Count > vParamInfo.Length))
432 			{
433 				Debug.Assert(false);
434 				bParamInfoValid = false;
435 			}
436 
437 			StringBuilder sb = new StringBuilder();
438 
439 			EcasCondition eCond = (ecasObj as EcasCondition);
440 			if(eCond != null)
441 			{
442 				if(eCond.Negate) sb.Append(KPRes.Not);
443 			}
444 
445 			for(int i = 0; i < ecasObj.Parameters.Count; ++i)
446 			{
447 				string strParam = ecasObj.Parameters[i];
448 				string strAppend;
449 
450 				if(bParamInfoValid)
451 				{
452 					EcasValueType t = vParamInfo[i].Type;
453 					if(t == EcasValueType.String)
454 						strAppend = strParam;
455 					else if(t == EcasValueType.Bool)
456 						strAppend = (StrUtil.StringToBool(strParam) ? KPRes.Yes : KPRes.No);
457 					else if(t == EcasValueType.EnumStrings)
458 					{
459 						uint uEnumID;
460 						if(uint.TryParse(strParam, out uEnumID) == false) { Debug.Assert(false); }
461 						EcasEnum ee = vParamInfo[i].EnumValues;
462 						if(ee != null) strAppend = ee.GetItemString(uEnumID, string.Empty);
463 						else { Debug.Assert(false); strAppend = strParam; }
464 					}
465 					else if(t == EcasValueType.Int64)
466 						strAppend = FilterTypeI64(strParam);
467 					else if(t == EcasValueType.UInt64)
468 						strAppend = FilterTypeU64(strParam);
469 					else { Debug.Assert(false); strAppend = strParam; }
470 				}
471 				else strAppend = strParam;
472 
473 				if(string.IsNullOrEmpty(strAppend)) continue;
474 				string strAppTrimmed = strAppend.Trim();
475 				if(strAppTrimmed.Length == 0) continue;
476 				if(sb.Length > 0) sb.Append(", ");
477 				sb.Append(strAppTrimmed);
478 			}
479 
480 			return sb.ToString();
481 		}
482 
CompareStrings(string x, string y, uint uCompareType)483 		public static bool CompareStrings(string x, string y, uint uCompareType)
484 		{
485 			if(x == null) { Debug.Assert(false); return false; }
486 			if(y == null) { Debug.Assert(false); return false; }
487 
488 			if(uCompareType == EcasUtil.StdStringCompareEquals)
489 				return x.Equals(y, StrUtil.CaseIgnoreCmp);
490 			if(uCompareType == EcasUtil.StdStringCompareContains)
491 				return (x.IndexOf(y, StrUtil.CaseIgnoreCmp) >= 0);
492 			if(uCompareType == EcasUtil.StdStringCompareStartsWith)
493 				return x.StartsWith(y, StrUtil.CaseIgnoreCmp);
494 			if(uCompareType == EcasUtil.StdStringCompareEndsWith)
495 				return x.EndsWith(y, StrUtil.CaseIgnoreCmp);
496 			if(uCompareType == EcasUtil.StdStringCompareRegEx)
497 			{
498 				try { return Regex.IsMatch(x, y, RegexOptions.IgnoreCase); }
499 				catch(Exception) { Debug.Assert(false); }
500 				return false;
501 			}
502 
503 			Debug.Assert(false); // Unknown compare type
504 			return false;
505 		}
506 
FilterTypeI64(string str)507 		private static string FilterTypeI64(string str)
508 		{
509 			if(string.IsNullOrEmpty(str)) return string.Empty;
510 
511 			long i64;
512 			if(long.TryParse(str, out i64)) return i64.ToString();
513 
514 			return string.Empty;
515 		}
516 
FilterTypeU64(string str)517 		private static string FilterTypeU64(string str)
518 		{
519 			if(string.IsNullOrEmpty(str)) return string.Empty;
520 
521 			ulong u64;
522 			if(ulong.TryParse(str, out u64)) return u64.ToString();
523 
524 			return string.Empty;
525 		}
526 	}
527 }
528