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