/// <summary> /// Return auto-type tokens without any field processing; i.e. UserName won't be converted to actual user name. /// If given auto-type text is null or empty, default is returned (UserName {Tab} Password {Tab} Enter). /// </summary> /// <param name="autotypeText">Auto-type text.</param> public static IEnumerable <AutotypeToken> GetUnexpandedAutotypeTokens(string autotypeText) { if (string.IsNullOrEmpty(autotypeText)) { yield return(new AutotypeToken("UserName", AutotypeTokenKind.Command)); yield return(new AutotypeToken("{Tab}")); yield return(new AutotypeToken("Password", AutotypeTokenKind.Command)); yield return(new AutotypeToken("{Enter}")); } else { var state = AutoTypeState.Default; string?command = null; var sbCommandArguments = new StringBuilder(); foreach (var ch in autotypeText) { switch (state) { case AutoTypeState.Default: if (ch == '\\') { state = AutoTypeState.Escape; } else { yield return(new AutotypeToken(ch.ToString())); } break; case AutoTypeState.Escape: switch (ch) { case 'u': case 'p': case '2': case 'g': case 'i': case 'l': case 'm': case 'z': //single character escape yield return(GetCommandToken(ch.ToString(), null)); state = AutoTypeState.Default; break; case 'c': //double character escape state = AutoTypeState.EscapeCreditCard; break; case 'b': yield return(new AutotypeToken("{Backspace}")); state = AutoTypeState.Default; break; case 't': yield return(new AutotypeToken("{Tab}")); state = AutoTypeState.Default; break; case 's': yield return(new AutotypeToken("+{Tab}")); state = AutoTypeState.Default; break; case 'n': yield return(new AutotypeToken("{Enter}")); state = AutoTypeState.Default; break; case 'd': case 'w': case 'W': //mandatory number characters command = ch.ToString(); state = AutoTypeState.EscapeMandatoryNumber; break; case 'o': //optional number characters command = ch.ToString(); state = AutoTypeState.EscapeOptionalNumber; break; default: //if escape doesn't exist yield return(new AutotypeToken(ch.ToString())); state = AutoTypeState.Default; break; } break; case AutoTypeState.EscapeCreditCard: switch (ch) { case 'n': case 't': case 'e': case 'v': case 'p': //double character escapes yield return(GetCommandToken("c" + ch.ToString(), null)); state = AutoTypeState.Default; break; default: //if escape doesn't exist foreach (var key in AutotypeToken.GetAutotypeTokensFromText("c" + ch)) { yield return(key); } state = AutoTypeState.Default; break; } break; case AutoTypeState.EscapeMandatoryNumber: if (char.IsDigit(ch)) { sbCommandArguments.Append(ch); state = AutoTypeState.EscapeOptionalNumber; } else { foreach (var key in AutotypeToken.GetAutotypeTokensFromText(command + sbCommandArguments.ToString() + ch)) { yield return(key); } command = null; state = AutoTypeState.Default; } break; case AutoTypeState.EscapeOptionalNumber: if (char.IsDigit(ch) && (sbCommandArguments.Length < 3)) { sbCommandArguments.Append(ch); } else { yield return(GetCommandToken(command !, sbCommandArguments.ToString())); command = null; sbCommandArguments.Length = 0; if (ch == '\\') { state = AutoTypeState.Escape; } else { yield return(new AutotypeToken(ch.ToString())); state = AutoTypeState.Default; } } break; default: throw new NotImplementedException("Unknown state"); } } if (command != null) { if ((sbCommandArguments.Length == 0) && (command.Equals("d", StringComparison.Ordinal) || command.Equals("w", StringComparison.Ordinal) || command.Equals("W", StringComparison.Ordinal))) { foreach (var key in AutotypeToken.GetAutotypeTokensFromText(command)) { yield return(key); } } else { yield return(GetCommandToken(command, sbCommandArguments.ToString())); } } else if (state == AutoTypeState.Escape) { yield return(new AutotypeToken(@"\")); } } }
/// <summary> /// Return auto-type tokens with textual fields filled in. Command fields, e.g. Wait, are not filled. /// Following commands are possible: /// * TwoFactorCode: 6-digit code for two-factor authentication. /// * Delay: Delay between characters in milliseconds. /// * Wait: Pause in milliseconds. /// * Legacy: Switches processing to legacy mode. /// </summary> /// <param name="tokens">Auto-type tokens.</param> /// <param name="entry">Entry to base fill-in on.</param> /// <exception cref="ArgumentNullException">Tokens cannot be null. -or- Entry cannot be null.</exception> internal static IEnumerable <AutotypeToken> GetAutotypeTokens(IEnumerable <AutotypeToken> tokens, Entry entry) { if (tokens == null) { throw new ArgumentNullException(nameof(tokens), "Tokens cannot be null."); } if (entry == null) { throw new ArgumentNullException(nameof(entry), "Entry cannot be null."); } foreach (var token in tokens) { if (token.Kind == AutotypeTokenKind.Command) { var parts = token.Content.Split(':'); var command = parts[0]; var argument = (parts.Length > 1) ? parts[1] : null; switch (command) { case "UserName": foreach (var key in AutotypeToken.GetAutotypeTokensFromText(entry.UserName)) { yield return(key); } break; case "Password": foreach (var key in AutotypeToken.GetAutotypeTokensFromText(entry.Password)) { yield return(key); } break; case "TwoFactorCode": yield return(token); break; case "CreditCardNumber": foreach (var key in AutotypeToken.GetAutotypeTokensFromText(TrimCreditCardNumber(entry.CreditCardNumber))) { yield return(key); } break; case "CreditCardNumberTabbed": foreach (var key in AutotypeToken.GetAutotypeTokensFromText(TrimCreditCardNumber(entry.CreditCardNumber, isTabbed: true))) { yield return(key); } break; case "CreditCardExpiration": foreach (var key in AutotypeToken.GetAutotypeTokensFromText(entry.CreditCardExpiration)) { yield return(key); } break; case "CreditCardVerificationValue": foreach (var key in AutotypeToken.GetAutotypeTokensFromText(entry.CreditCardVerificationValue)) { yield return(key); } break; case "CreditCardPin": foreach (var key in AutotypeToken.GetAutotypeTokensFromText(entry.CreditCardPin)) { yield return(key); } break; case "Group": foreach (var key in AutotypeToken.GetAutotypeTokensFromText(entry.Group)) { yield return(key); } break; case "Title": foreach (var key in AutotypeToken.GetAutotypeTokensFromText(entry.Title)) { yield return(key); } break; case "Url": foreach (var key in AutotypeToken.GetAutotypeTokensFromText(entry.Url)) { yield return(key); } break; case "Email": foreach (var key in AutotypeToken.GetAutotypeTokensFromText(entry.Email)) { yield return(key); } break; case "Notes": var noteLines = entry.Notes.Split(new string[] { "\r\n", "\n", "\r" }, StringSplitOptions.None); if (string.IsNullOrEmpty(argument)) { foreach (var key in AutotypeToken.GetAutotypeTokensFromText(string.Join("\n", noteLines))) { yield return(key); } } else { if (int.TryParse(argument, NumberStyles.Integer, CultureInfo.InvariantCulture, out var lineNumber)) { if (lineNumber <= noteLines.Length) { var lineText = noteLines[lineNumber - 1]; if (lineText.Length > 0) { foreach (var key in AutotypeToken.GetAutotypeTokensFromText(lineText)) { yield return(key); } } } } } break; case "Delay": yield return(token); break; case "Wait": yield return(token); break; case "Legacy": yield return(token); break; } } else { yield return(token); } } }