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.Media; 24 using System.Text; 25 using System.Threading; 26 using System.Windows.Forms; 27 28 using KeePass.Native; 29 using KeePass.Resources; 30 using KeePass.Util.SendInputExt; 31 32 using KeePassLib.Utility; 33 34 using NativeLib = KeePassLib.Native.NativeLib; 35 36 namespace KeePass.Util 37 { 38 internal enum SiEventType 39 { 40 None = 0, 41 Key, 42 KeyModifier, 43 Char, 44 Delay, 45 SetDefaultDelay, 46 ClipboardCopy, 47 AppActivate, 48 Beep 49 } 50 51 internal sealed class SiEvent 52 { 53 public SiEventType Type = SiEventType.None; 54 55 public int VKey = 0; 56 public bool? ExtendedKey = null; 57 58 public Keys KeyModifier = Keys.None; 59 60 public char Char = char.MinValue; 61 62 public bool? Down = null; 63 64 public uint Delay = 0; 65 66 public string Text = null; 67 68 #if DEBUG 69 // For debugger display ToString()70 public override string ToString() 71 { 72 string str = Enum.GetName(typeof(SiEventType), this.Type); 73 74 string strSub = null; 75 switch(this.Type) 76 { 77 case SiEventType.Key: 78 strSub = this.VKey.ToString() + " " + this.ExtendedKey.ToString() + 79 " " + this.Down.ToString(); 80 break; 81 case SiEventType.KeyModifier: 82 strSub = this.KeyModifier.ToString() + " " + this.Down.ToString(); 83 break; 84 case SiEventType.Char: 85 strSub = this.Char.ToString() + " " + this.Down.ToString(); 86 break; 87 case SiEventType.Delay: 88 case SiEventType.SetDefaultDelay: 89 strSub = this.Delay.ToString(); 90 break; 91 case SiEventType.ClipboardCopy: 92 case SiEventType.AppActivate: 93 case SiEventType.Beep: 94 strSub = this.Text; 95 break; 96 default: break; 97 } 98 99 if(!string.IsNullOrEmpty(strSub)) return (str + ": " + strSub); 100 return str; 101 } 102 #endif 103 } 104 105 public static class SendInputEx 106 { 107 private static CriticalSectionEx g_csSending = new CriticalSectionEx(); 108 109 private static int g_cCurSending = 0; 110 public static bool IsSending 111 { 112 get { return (g_cCurSending != 0); } 113 } 114 115 public static event EventHandler<SiEventArgs> CreateEngine; 116 SendKeysWait(string strKeys, bool bObfuscate)117 public static void SendKeysWait(string strKeys, bool bObfuscate) 118 { 119 if(strKeys == null) { Debug.Assert(false); return; } 120 121 List<SiEvent> l = Parse(strKeys); 122 if(l.Count == 0) return; 123 124 if(bObfuscate) SiObf.Obfuscate(l); 125 126 FixEventSeq(l); 127 128 ISiEngine si = null; 129 if(SendInputEx.CreateEngine != null) 130 { 131 SiEventArgs ea = new SiEventArgs(); 132 SendInputEx.CreateEngine(null, ea); 133 si = ea.Engine; 134 } 135 if(si == null) 136 { 137 if(NativeLib.IsUnix()) si = new SiEngineUnix(); 138 else si = new SiEngineWin(); 139 } 140 141 bool bInter = Program.Config.Integration.AutoTypeAllowInterleaved; 142 if(!bInter) 143 { 144 if(!g_csSending.TryEnter()) return; 145 } 146 147 Interlocked.Increment(ref g_cCurSending); 148 try 149 { 150 si.Init(); 151 Send(si, l); 152 } 153 finally 154 { 155 try { si.Release(); } 156 catch(Exception) { Debug.Assert(false); } 157 158 Interlocked.Decrement(ref g_cCurSending); 159 160 if(!bInter) g_csSending.Exit(); 161 } 162 } 163 Parse(string strSequence)164 private static List<SiEvent> Parse(string strSequence) 165 { 166 CharStream cs = new CharStream(strSequence); 167 List<SiEvent> l = new List<SiEvent>(); 168 string strError = KPRes.AutoTypeSequenceInvalid; 169 170 Keys kCurKbMods = Keys.None; 171 172 List<Keys> lMods = new List<Keys>(); 173 lMods.Add(Keys.None); 174 175 while(true) 176 { 177 char ch = cs.ReadChar(); 178 if(ch == char.MinValue) break; 179 180 if((ch == '+') || (ch == '^') || (ch == '%')) 181 { 182 if(lMods.Count == 0) { Debug.Assert(false); break; } 183 else if(ch == '+') lMods[lMods.Count - 1] |= Keys.Shift; 184 else if(ch == '^') lMods[lMods.Count - 1] |= Keys.Control; 185 else if(ch == '%') lMods[lMods.Count - 1] |= Keys.Alt; 186 else { Debug.Assert(false); } 187 188 continue; 189 } 190 else if(ch == '(') 191 { 192 lMods.Add(Keys.None); 193 continue; 194 } 195 else if(ch == ')') 196 { 197 if(lMods.Count >= 2) 198 { 199 lMods.RemoveAt(lMods.Count - 1); 200 lMods[lMods.Count - 1] = Keys.None; 201 } 202 else throw new FormatException(strError); 203 204 continue; 205 } 206 207 Keys kEffMods = Keys.None; 208 foreach(Keys k in lMods) kEffMods |= k; 209 210 EnsureKeyModifiers(kEffMods, ref kCurKbMods, l); 211 212 if(ch == '{') 213 { 214 List<SiEvent> lSub = ParseSpecial(cs); 215 if(lSub == null) throw new FormatException(strError); 216 217 l.AddRange(lSub); 218 } 219 else if(ch == '}') 220 throw new FormatException(strError); 221 else if(ch == '~') 222 { 223 SiEvent si = new SiEvent(); 224 si.Type = SiEventType.Key; 225 si.VKey = (int)Keys.Enter; 226 227 l.Add(si); 228 } 229 else 230 { 231 SiEvent si = new SiEvent(); 232 si.Type = SiEventType.Char; 233 si.Char = ch; 234 235 l.Add(si); 236 } 237 238 lMods[lMods.Count - 1] = Keys.None; 239 } 240 241 EnsureKeyModifiers(Keys.None, ref kCurKbMods, l); 242 243 return l; 244 } 245 EnsureKeyModifiers(Keys kReqMods, ref Keys kCurKbMods, List<SiEvent> l)246 private static void EnsureKeyModifiers(Keys kReqMods, ref Keys kCurKbMods, 247 List<SiEvent> l) 248 { 249 if(kReqMods == kCurKbMods) return; 250 251 if((kReqMods & Keys.Shift) != (kCurKbMods & Keys.Shift)) 252 { 253 SiEvent si = new SiEvent(); 254 si.Type = SiEventType.KeyModifier; 255 si.KeyModifier = Keys.Shift; 256 si.Down = ((kReqMods & Keys.Shift) != Keys.None); 257 258 l.Add(si); 259 } 260 261 if((kReqMods & Keys.Control) != (kCurKbMods & Keys.Control)) 262 { 263 SiEvent si = new SiEvent(); 264 si.Type = SiEventType.KeyModifier; 265 si.KeyModifier = Keys.Control; 266 si.Down = ((kReqMods & Keys.Control) != Keys.None); 267 268 l.Add(si); 269 } 270 271 if((kReqMods & Keys.Alt) != (kCurKbMods & Keys.Alt)) 272 { 273 SiEvent si = new SiEvent(); 274 si.Type = SiEventType.KeyModifier; 275 si.KeyModifier = Keys.Alt; 276 si.Down = ((kReqMods & Keys.Alt) != Keys.None); 277 278 l.Add(si); 279 } 280 281 kCurKbMods = kReqMods; 282 } 283 ParseSpecial(CharStream cs)284 private static List<SiEvent> ParseSpecial(CharStream cs) 285 { 286 // Skip leading white space 287 while(true) 288 { 289 char ch = cs.PeekChar(); 290 if(ch == char.MinValue) { Debug.Assert(false); return null; } 291 292 if(!char.IsWhiteSpace(ch)) break; 293 cs.ReadChar(); 294 } 295 296 // First char is *always* part of the name (support for "{{}", etc.) 297 char chFirst = cs.ReadChar(); 298 if(chFirst == char.MinValue) { Debug.Assert(false); return null; } 299 300 int iPart = 0; 301 StringBuilder sbName = new StringBuilder(), sbParams = 302 new StringBuilder(); 303 sbName.Append(chFirst); 304 305 while(true) 306 { 307 char ch = cs.ReadChar(); 308 if(ch == char.MinValue) { Debug.Assert(false); return null; } 309 if(ch == '}') break; 310 311 if(iPart == 0) 312 { 313 if(char.IsWhiteSpace(ch)) ++iPart; 314 else sbName.Append(ch); 315 } 316 else sbParams.Append(ch); 317 } 318 319 string strName = sbName.ToString(); 320 string strParams = sbParams.ToString().Trim(); 321 322 uint? ouParam = null; 323 if(strParams.Length > 0) 324 { 325 uint uParamTry; 326 if(uint.TryParse(strParams, out uParamTry)) ouParam = uParamTry; 327 } 328 329 List<SiEvent> l = new List<SiEvent>(); 330 331 if(strName.Equals("DELAY", StrUtil.CaseIgnoreCmp)) 332 { 333 if(!ouParam.HasValue) { Debug.Assert(false); return null; } 334 335 SiEvent si = new SiEvent(); 336 si.Type = SiEventType.Delay; 337 si.Delay = ouParam.Value; 338 339 l.Add(si); 340 return l; 341 } 342 if(strName.StartsWith("DELAY=", StrUtil.CaseIgnoreCmp)) 343 { 344 SiEvent si = new SiEvent(); 345 si.Type = SiEventType.SetDefaultDelay; 346 347 string strDelay = strName.Substring(6).Trim(); 348 uint uDelay; 349 if(uint.TryParse(strDelay, out uDelay)) 350 si.Delay = uDelay; 351 else { Debug.Assert(false); return null; } 352 353 l.Add(si); 354 return l; 355 } 356 if(strName.Equals("VKEY", StrUtil.CaseIgnoreCmp) || 357 strName.Equals("VKEY-NX", StrUtil.CaseIgnoreCmp) || 358 strName.Equals("VKEY-EX", StrUtil.CaseIgnoreCmp)) 359 { 360 SiEvent si = CreateVKeyEvent(strParams); 361 if(si == null) { Debug.Assert(false); return null; } 362 363 // VKEY-NX and VKEY-EX are supported for backward compatibility; 364 // new syntax: {VKEY K N} and {VKEY K E} 365 if(strName.EndsWith("-NX", StrUtil.CaseIgnoreCmp)) 366 si.ExtendedKey = false; 367 else if(strName.EndsWith("-EX", StrUtil.CaseIgnoreCmp)) 368 si.ExtendedKey = true; 369 370 l.Add(si); 371 return l; 372 } 373 if(strName.Equals("APPACTIVATE", StrUtil.CaseIgnoreCmp)) 374 { 375 SiEvent si = new SiEvent(); 376 si.Type = SiEventType.AppActivate; 377 si.Text = strParams; 378 379 l.Add(si); 380 return l; 381 } 382 if(strName.Equals("BEEP", StrUtil.CaseIgnoreCmp)) 383 { 384 SiEvent si = new SiEvent(); 385 si.Type = SiEventType.Beep; 386 si.Text = strParams; 387 388 l.Add(si); 389 return l; 390 } 391 392 SiCode siCode = SiCodes.Get(strName); 393 394 SiEvent siTmpl = new SiEvent(); 395 if(siCode != null) 396 { 397 siTmpl.Type = SiEventType.Key; 398 siTmpl.VKey = siCode.VKey; 399 siTmpl.ExtendedKey = siCode.ExtKey; 400 } 401 else if(strName.Length == 1) 402 { 403 siTmpl.Type = SiEventType.Char; 404 siTmpl.Char = strName[0]; 405 } 406 else 407 { 408 throw new FormatException(KPRes.AutoTypeUnknownPlaceholder + 409 MessageService.NewLine + @"{" + strName + @"}"); 410 } 411 412 uint uRepeat = ouParam.GetValueOrDefault(1); 413 for(uint u = 0; u < uRepeat; ++u) 414 { 415 SiEvent si = new SiEvent(); 416 si.Type = siTmpl.Type; 417 si.VKey = siTmpl.VKey; 418 si.ExtendedKey = siTmpl.ExtendedKey; 419 si.Char = siTmpl.Char; 420 421 l.Add(si); 422 } 423 424 return l; 425 } 426 FixEventSeq(List<SiEvent> l)427 private static void FixEventSeq(List<SiEvent> l) 428 { 429 // Convert chars to keys 430 // Keys kMod = Keys.None; 431 for(int i = 0; i < l.Count; ++i) 432 { 433 SiEvent si = l[i]; 434 SiEventType t = si.Type; 435 436 // if(t == SiEventType.KeyModifier) 437 // { 438 // if(!si.Down.HasValue) { Debug.Assert(false); continue; } 439 // if(si.Down.Value) 440 // { 441 // Debug.Assert((kMod & si.KeyModifier) == Keys.None); 442 // kMod |= si.KeyModifier; 443 // } 444 // else 445 // { 446 // Debug.Assert((kMod & si.KeyModifier) == si.KeyModifier); 447 // kMod &= ~si.KeyModifier; 448 // } 449 // } 450 if(t == SiEventType.Char) 451 { 452 // bool bLightConv = (kMod == Keys.None); 453 int iVKey = SiCodes.CharToVKey(si.Char, true); 454 if(iVKey > 0) 455 { 456 si.Type = SiEventType.Key; 457 si.VKey = iVKey; 458 } 459 } 460 } 461 } 462 Send(ISiEngine siEngine, List<SiEvent> l)463 private static void Send(ISiEngine siEngine, List<SiEvent> l) 464 { 465 bool bHasClipOp = l.Exists(SendInputEx.IsClipboardOp); 466 ClipboardEventChainBlocker cev = null; 467 ClipboardContents cnt = null; 468 if(bHasClipOp) 469 { 470 cev = new ClipboardEventChainBlocker(); 471 cnt = new ClipboardContents(true, true); 472 } 473 474 try { SendPriv(siEngine, l); } 475 finally 476 { 477 if(bHasClipOp) 478 { 479 ClipboardUtil.Clear(); 480 cnt.SetData(); 481 cev.Dispose(); 482 } 483 } 484 } 485 IsClipboardOp(SiEvent si)486 private static bool IsClipboardOp(SiEvent si) 487 { 488 if(si == null) { Debug.Assert(false); return false; } 489 return (si.Type == SiEventType.ClipboardCopy); 490 } 491 SendPriv(ISiEngine siEngine, List<SiEvent> l)492 private static void SendPriv(ISiEngine siEngine, List<SiEvent> l) 493 { 494 // For 2000 alphanumeric characters: 495 // * KeePass 1.26: 0:31 min 496 // * KeePass 2.24: 1:58 min 497 // * New engine of KeePass 2.25 with default delay DD: 498 // * DD = 1: 0:31 min 499 // * DD = 31: 1:03 min 500 // * DD = 32: 1:34 min 501 // * DD = 33: 1:34 min 502 // * DD = 43: 1:34 min 503 // * DD = 46: 1:34 min 504 // * DD = 47: 2:05 min 505 // * DD = 49: 2:05 min 506 // * DD = 59: 2:05 min 507 uint uDefaultDelay = 33; // Slice boundary + 1 508 509 // Induced by SiEngineWin.TrySendCharByKeypresses 510 uDefaultDelay += 2; 511 512 int iDefOvr = Program.Config.Integration.AutoTypeInterKeyDelay; 513 if(iDefOvr >= 0) 514 { 515 if(iDefOvr == 0) iDefOvr = 1; // 1 ms is minimum 516 uDefaultDelay = (uint)iDefOvr; 517 } 518 519 bool bFirstInput = true; 520 foreach(SiEvent si in l) 521 { 522 // Also delay key modifiers, as a workaround for applications 523 // with broken time-dependent message processing; 524 // https://sourceforge.net/p/keepass/bugs/1213/ 525 if((si.Type == SiEventType.Key) || (si.Type == SiEventType.Char) || 526 (si.Type == SiEventType.KeyModifier)) 527 { 528 if(!bFirstInput) 529 siEngine.Delay(uDefaultDelay); 530 531 bFirstInput = false; 532 } 533 534 switch(si.Type) 535 { 536 case SiEventType.Key: 537 siEngine.SendKey(si.VKey, si.ExtendedKey, si.Down); 538 break; 539 540 case SiEventType.KeyModifier: 541 if(si.Down.HasValue) 542 siEngine.SetKeyModifier(si.KeyModifier, si.Down.Value); 543 else { Debug.Assert(false); } 544 break; 545 546 case SiEventType.Char: 547 siEngine.SendChar(si.Char, si.Down); 548 break; 549 550 case SiEventType.Delay: 551 siEngine.Delay(si.Delay); 552 break; 553 554 case SiEventType.SetDefaultDelay: 555 uDefaultDelay = si.Delay; 556 break; 557 558 case SiEventType.ClipboardCopy: 559 if(!string.IsNullOrEmpty(si.Text)) 560 ClipboardUtil.Copy(si.Text, false, false, null, 561 null, IntPtr.Zero); 562 else if(si.Text != null) 563 ClipboardUtil.Clear(); 564 break; 565 566 case SiEventType.AppActivate: 567 AppActivate(si); 568 break; 569 570 case SiEventType.Beep: 571 Beep(si); 572 break; 573 574 default: 575 Debug.Assert(false); 576 break; 577 } 578 579 // Extra delay after tabs 580 if(((si.Type == SiEventType.Key) && (si.VKey == (int)Keys.Tab)) || 581 ((si.Type == SiEventType.Char) && (si.Char == '\t'))) 582 { 583 if(uDefaultDelay < 100) 584 siEngine.Delay(uDefaultDelay); 585 } 586 } 587 } 588 AppActivate(SiEvent si)589 private static void AppActivate(SiEvent si) 590 { 591 try 592 { 593 if(string.IsNullOrEmpty(si.Text)) return; 594 595 IntPtr h = NativeMethods.FindWindow(si.Text); 596 if(h != IntPtr.Zero) 597 NativeMethods.EnsureForegroundWindow(h); 598 } 599 catch(Exception) { Debug.Assert(false); } 600 } 601 Beep(SiEvent si)602 private static void Beep(SiEvent si) 603 { 604 try 605 { 606 string str = si.Text; 607 if(string.IsNullOrEmpty(str)) 608 { 609 SystemSounds.Beep.Play(); 610 return; 611 } 612 613 string[] v = str.Split(new char[] { ' ', '\t' }, 614 StringSplitOptions.RemoveEmptyEntries); 615 616 int f = 800, d = 200; // Defaults of Console.Beep() 617 if(v.Length >= 1) int.TryParse(v[0], out f); 618 if(v.Length >= 2) int.TryParse(v[1], out d); 619 620 f = Math.Min(Math.Max(f, 37), 32767); 621 if(d <= 0) return; 622 623 Console.Beep(f, d); // Throws on a server 624 } 625 catch(Exception) { Debug.Assert(false); } 626 } 627 CreateVKeyEvent(string strParams)628 private static SiEvent CreateVKeyEvent(string strParams) 629 { 630 string[] v = (strParams ?? string.Empty).Trim().Split( 631 new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); 632 if((v == null) || (v.Length < 1)) { Debug.Assert(false); return null; } 633 634 SiEvent si = new SiEvent(); 635 si.Type = SiEventType.Key; 636 637 int k; 638 if(!StrUtil.TryParseInt(v[0], out k)) { Debug.Assert(false); return null; } 639 if(k < 0) { Debug.Assert(false); return null; } 640 si.VKey = k; 641 642 for(int i = 1; i < v.Length; ++i) 643 { 644 string strParam = v[i]; 645 if(string.IsNullOrEmpty(strParam)) { Debug.Assert(false); continue; } 646 647 if(strParam.IndexOf('=') < 0) 648 { 649 bool bExt = (strParam.IndexOf('E') >= 0); 650 bool bNonExt = (strParam.IndexOf('N') >= 0); 651 if(bExt && !bNonExt) si.ExtendedKey = true; 652 if(!bExt && bNonExt) si.ExtendedKey = false; 653 // The default is null => automatic decision 654 655 bool bDown = (strParam.IndexOf('D') >= 0); 656 bool bUp = (strParam.IndexOf('U') >= 0); 657 if(bDown && !bUp) si.Down = true; 658 if(!bDown && bUp) si.Down = false; 659 // The default is null => down and up 660 } 661 662 // Named parameters with values could be implemented like 663 // {VKEY X ED R=5 DA=100}, and parameters without a '=' 664 // could be treated as flags (so {VKEY X ED} = {VKEY X E D}) 665 } 666 667 return si; 668 } 669 } 670 } 671