예제 #1
0
        /// <summary>
        /// Determines whether this is in a currently valid save-able state.
        /// </summary>
        /// <param name="baseEntry">The entry we would like to save to.</param>
        /// <returns>Whether a save is possible.</returns>
        private bool CanSave(IKeePassEntry baseEntry)
        {
            ValidationError = String.Empty;

            if (String.IsNullOrEmpty(WorkingCopy.Key))
            {
                ValidationError = this.LocalizedMissingKey;
            }

            if (FieldEditorViewModel.InvalidNames.Contains(WorkingCopy.Key))
            {
                ValidationError = this.LocalizedReservedKey;
            }

            if (baseEntry != null)
            {
                // If this is a new string, or if we've changed the key from the original,
                // validate that it doesn't clash with other strings in the entry's set.
                if ((Original == null || Original.Key != WorkingCopy.Key) &&
                    baseEntry.Fields.Select(field => field.Key).Contains(WorkingCopy.Key))
                {
                    ValidationError = this.LocalizedDuplicateKey;
                }
            }

            return(String.IsNullOrEmpty(ValidationError));
        }
예제 #2
0
        public void HistoryStartsEmpty()
        {
            IKeePassEntry newEntry = GetNewEntry();

            Assert.IsNotNull(newEntry.History, "New history should not be null");
            Assert.AreEqual(0, newEntry.History.Entries.Count, "New history should be empty");
        }
예제 #3
0
        /// <summary>
        /// Handles clicks on the Child GridView.
        /// </summary>
        /// <param name="sender">The Child GridView.</param>
        /// <param name="e">ClickEventArgs for the action.</param>
        private void ChildGridView_ItemClick(object sender, ItemClickEventArgs e)
        {
            bool wasFiltered = !String.IsNullOrEmpty(this.searchBox.Text);

            if (wasFiltered)
            {
                this.searchBox.Text = String.Empty;
            }

            // First check to see if it's an entry
            if (e.ClickedItem is IDatabaseEntryViewModel clickedEntry)
            {
                IKeePassEntry entry = clickedEntry.Node as IKeePassEntry;
                DebugHelper.Assert(entry != null);

                if (wasFiltered)
                {
                    ViewModel.NavigationViewModel.SetGroup(entry.Parent);
                }

                // For now, on item click, navigate to the EntryDetailsView.
                Frame.Navigate(
                    typeof(EntryDetailsView),
                    ViewModel.GetEntryDetailsViewModel(entry, /* editing */ false)
                    );
            }
            else
            {
                // We clicked a group, so drill into it...
                IDatabaseGroupViewModel clickedGroup = e.ClickedItem as IDatabaseGroupViewModel;
                DebugHelper.Assert(clickedGroup != null);

                clickedGroup.RequestOpenCommand.Execute(null);
            }
        }
예제 #4
0
        public void PlaceholderTest()
        {
            IKeePassEntry entry = GetTestEntry();

            Assert.AreEqual(
                $"this is a test string with scheme http and {entry.Title.ClearValue}",
                PlaceholderResolver.Resolve("this is a test string {C:comment here}with scheme {URL:SCM} and {TITLE}", entry)
            );
        }
예제 #5
0
        public void PlaceholderE2EUrlTest()
        {
            IKeePassEntry entry = GetTestEntry();
            entry.OverrideUrl = "http://{C:test}example.com/{URL:HOST}";

            Assert.AreEqual(
                "http://example.com/www.example.com",
                entry.ConstructUriString()
            );
        }
예제 #6
0
        /// <summary>
        /// Attempts to construct a URI string from either <paramref name="entry"/>'s URL or override URL.
        /// </summary>
        /// <param name="entry">The entry to construct a URI for.</param>
        /// <returns>The constructed URI string.</returns>
        public static string ConstructUriString(this IKeePassEntry entry)
        {
            string uriCandidate = entry.OverrideUrl;

            if (String.IsNullOrEmpty(uriCandidate))
            {
                uriCandidate = entry.Url?.ClearValue ?? string.Empty;
            }

            return(PlaceholderResolver.Resolve(uriCandidate, entry));
        }
예제 #7
0
        public void SyncTo(IKeePassEntry newEntry, bool isUpdate = true)
        {
            DebugHelper.Assert(newEntry != null);
            if (newEntry == null)
            {
                throw new ArgumentNullException(nameof(newEntry));
            }

            if (isUpdate)
            {
                DebugHelper.Assert(!this.isHistoryEntry);
                if (!this.isHistoryEntry)
                {
                    if (History == null)
                    {
                        History = new KdbxHistory(this._metadata);
                    }

                    History.Add(this);
                }
            }

            IconID          = newEntry.IconID;
            CustomIconUuid  = newEntry.CustomIconUuid;
            ForegroundColor = newEntry.ForegroundColor;
            BackgroundColor = newEntry.BackgroundColor;
            OverrideUrl     = newEntry.OverrideUrl;
            Tags            = newEntry.Tags;

            Title    = newEntry.Title?.Clone();
            UserName = newEntry.UserName?.Clone();
            Password = newEntry.Password?.Clone();
            Url      = newEntry.Url?.Clone();
            Notes    = newEntry.Notes?.Clone();

            /*Fields.Clear();
             * foreach(IProtectedString str in newEntry.Fields.Select(f => f.Clone()))
             * {
             *  Fields.Add(str);
             * }*/
            Fields = new ObservableCollection <IProtectedString>(newEntry.Fields.Select(f => f.Clone()));

            Binaries = newEntry.Binaries;
            AutoType = newEntry.AutoType;

            Times.SyncTo(newEntry.Times);

            if (isUpdate)
            {
                Times.LastModificationTime = DateTime.Now;
            }
        }
예제 #8
0
        public void DatabaseNavigationViewModel_SetGroupPrunes()
        {
            IKeePassEntry activeEntry = this.document.Root.DatabaseGroup.GetChildEntry(0, 0, 0);

            this.viewModel.SetEntry(activeEntry);
            Assert.AreEqual(activeEntry, this.viewModel.ActiveLeaf, "ActiveLeaf should be the expected Entry after setting");

            this.viewModel.SetGroup(activeEntry.Parent);
            Assert.AreEqual(activeEntry, this.viewModel.ActiveLeaf, "ActiveLeaf should not change when SetGroup is a no-op");

            this.viewModel.SetGroup(this.document.Root.DatabaseGroup);
            Assert.IsNull(this.viewModel.ActiveLeaf, "ActiveLeaf should be null after setting a different active group");
        }
        /// <summary>
        /// Sets the last Breadcrumb to an Entry's parent, resets the leaves, and flags the Entry as active.
        /// </summary>
        /// <param name="entry">The entry to activate.</param>
        public void SetEntry(IKeePassEntry entry)
        {
            if (entry == null)
            {
                SetGroup(null);
            }
            else
            {
                SetGroup(entry.Parent);
            }

            ActiveLeaf = entry;
        }
예제 #10
0
        /// <summary>
        /// Initializes the ViewModel.
        /// </summary>
        /// <param name="entry">The database entry to proxy.</param>
        /// <param name="isReadOnly">Whether the database can currently be edited.</param>
        /// <param name="clipboardService">Clipboard service used for requesting credential copies.</param>
        /// <param name="settingsService">Service used to check settings for launching URLs.</param>
        public DatabaseEntryViewModel(
            IKeePassEntry entry,
            bool isReadOnly,
            ISensitiveClipboardService clipboardService,
            IAppSettingsService settingsService
            ) : base(entry, isReadOnly)
        {
            if (entry == null)
            {
                throw new ArgumentNullException(nameof(entry));
            }

            this.clipboardService = clipboardService ?? throw new ArgumentNullException(nameof(clipboardService));
            this.settingsService  = settingsService ?? throw new ArgumentNullException(nameof(settingsService));
            this.entryUri         = entry.GetLaunchableUri();

            RequestCopyUsernameCommand = new ActionCommand(
                () =>
            {
                this.clipboardService.CopyCredential(((IKeePassEntry)Node).UserName.ClearValue, ClipboardOperationType.UserName);
            }
                );

            RequestCopyPasswordCommand = new ActionCommand(
                () =>
            {
                this.clipboardService.CopyCredential(((IKeePassEntry)Node).Password.ClearValue, ClipboardOperationType.UserName);
            }
                );

            RequestCopyUrlCommand = new ActionCommand(
                () =>
            {
                this.clipboardService.CopyCredential(((IKeePassEntry)Node).ConstructUriString(), ClipboardOperationType.Other);
            }
                );

            RequestLaunchUrlCommand = new ActionCommand(
                () => this.entryUri != null,
                async() =>
            {
                if (this.settingsService.CopyPasswordOnUrlOpen)
                {
                    RequestCopyPasswordCommand.Execute(null);
                }
                await Launcher.LaunchUriAsync(this.entryUri);
            }
                );
        }
예제 #11
0
        public DesignDatabaseViewModel()
        {
            NavigationViewModel = new DatabaseNavigationViewModel();
            SortMode            = new DatabaseSortMode(DatabaseSortMode.Mode.DatabaseOrder, "Database order");
            AvailableSortModes  = new List <DatabaseSortMode>
            {
                SortMode
            };

            IKeePassGroup dbGroup  = GetGroup("Database");
            IKeePassGroup subGroup = GetGroup("Subdirectory", dbGroup);

            dbGroup.Children.Add(subGroup);
            IKeePassGroup rootGroup = GetGroup("Current Root", subGroup);

            subGroup.Children.Add(rootGroup);

            rootGroup.Children.Add(GetGroup("Foo Directory", rootGroup));
            rootGroup.Children.Add(GetGroup("Bar Directory", rootGroup));
            rootGroup.Children.Add(GetGroup("Baz Directory", rootGroup));
            rootGroup.Children.Add(GetGroup("Some Directory", rootGroup));
            rootGroup.Children.Add(GetGroup("Some other node", rootGroup));
            rootGroup.Children.Add(GetGroup("Foo Directory", rootGroup));
            rootGroup.Children.Add(GetGroup("Bar Directory", rootGroup));
            rootGroup.Children.Add(GetGroup("Baz Directory", rootGroup));
            rootGroup.Children.Add(GetGroup("Some Directory", rootGroup));
            rootGroup.Children.Add(GetGroup("Some other node", rootGroup));
            rootGroup.Children.Add(GetGroup("Foo Directory", rootGroup));
            rootGroup.Children.Add(GetGroup("Bar Directory", rootGroup));
            rootGroup.Children.Add(GetGroup("Baz Directory", rootGroup));
            rootGroup.Children.Add(GetGroup("Some Directory", rootGroup));
            rootGroup.Children.Add(GetGroup("Some other node", rootGroup));
            rootGroup.Children.Add(GetEntry("Bank", "welcome", parent: rootGroup));
            rootGroup.Children.Add(GetEntry("Airline", "flymeout", "123456", "myairline.org", parent: rootGroup));
            rootGroup.Children.Add(GetEntry("Facebook", "aloha", parent: rootGroup));

            IKeePassEntry active = GetEntry("FooHub", "secure89", "Jimbo", "http://test.com/", parent: rootGroup);

            rootGroup.Children.Add(active);

            NavigationViewModel.SetEntry(active);
            SortedChildren = new ReadOnlyObservableCollection <IDatabaseNodeViewModel>(
                new ObservableCollection <IDatabaseNodeViewModel>(
                    NavigationViewModel.ActiveGroup.Children
                    .Select(node => new DatabaseNodeViewModel(node, false)))
                );
        }
예제 #12
0
        public void HistoryUpdatesOnEntryUpdate()
        {
            IKeePassEntry newEntry = GetNewEntry();

            newEntry.Title.ClearValue = "Foo";

            IKeePassEntry editedEntry = newEntry.Clone();

            editedEntry.Title.ClearValue = "Bar";

            newEntry.SyncTo(editedEntry);
            Assert.AreEqual(1, newEntry.History.Entries.Count);
            Assert.AreEqual(0, editedEntry.History.Entries.Count);

            Assert.AreEqual("Bar", newEntry.Title.ClearValue);
            Assert.AreEqual("Foo", newEntry.History.Entries[0].Title.ClearValue);
        }
예제 #13
0
        /// <summary>
        /// Attempts to construct a URI from either <paramref name="entry"/>'s URL or override URL.
        /// </summary>
        /// <param name="entry">The entry to construct a URI for.</param>
        /// <returns>A launchable URI, or null.</returns>
        public static Uri GetLaunchableUri(this IKeePassEntry entry)
        {
            string uriCandidate = entry.ConstructUriString();

            if (uriCandidate == null)
            {
                return(null);
            }

            try
            {
                Uri uri = new Uri(uriCandidate);
                return(uri);
            }
            catch (FormatException)
            {
                return(null);
            }
        }
예제 #14
0
        public async Task DatabaseViewModel_CopyPassword()
        {
            IKeePassEntry entry = this.viewModel.Document.Root.DatabaseGroup.GetChildEntry(1, 1, 1);

            TaskCompletionSource <object> tcs = new TaskCompletionSource <object>();

            this.clipboardService.CredentialCopied += async(cs, typ) =>
            {
                Assert.AreEqual(ClipboardOperationType.Password, typ, "CopyType should have been Password");

                string clipboardContent = await this.clipboard.GetContentAsText();

                Assert.AreEqual(entry.Password.ClearValue, clipboardContent, "Clipboard content should be the entry's password");

                tcs.SetResult(null);
            };

            this.viewModel.RequestCopyPasswordCommand.Execute(entry);
            await tcs.Task;
        }
예제 #15
0
 public void BadPlaceholders()
 {
     IKeePassEntry entry = GetTestEntry();
     Assert.AreEqual(
         "foo{username",
         PlaceholderResolver.Resolve("foo{username", entry)
     );
     Assert.AreEqual(
         "foo{username{",
         PlaceholderResolver.Resolve("foo{username{", entry)
     );
     Assert.AreEqual(
         "foo{BAR}",
         PlaceholderResolver.Resolve("foo{BAR}", entry)
     );
     Assert.AreEqual(
         $"{{USERNAME{entry.UserName.ClearValue}}}",
         PlaceholderResolver.Resolve("{USERNAME{UsErnAME}}", entry)
     );
     Assert.AreEqual(
         $"{entry.Password.ClearValue}}}{{{entry.Title.ClearValue}",
         PlaceholderResolver.Resolve("{pASsword}}{{TITLE}", entry)
     );
     Assert.AreEqual(
         $"{{{{{{{{{entry.Password.ClearValue}{{}}{{}}}}}}}}}}}}}}}}{{{{}}{{",
         PlaceholderResolver.Resolve("{{{{{PASSWORD}{}{}}}}}}}}{{}{", entry)
     );
     Assert.AreEqual(
         "{C}",
         PlaceholderResolver.Resolve("{C}", entry)
     );
     Assert.AreEqual(
         string.Empty,
         PlaceholderResolver.Resolve("{C:}", entry)
     );
     Assert.AreEqual(
         string.Empty,
         PlaceholderResolver.Resolve("{C::asdf:::}", entry)
     );
 }
예제 #16
0
        /// <summary>
        /// Attempts to save this field to the specified entry.
        /// </summary>
        /// <param name="baseEntry">The entry to save to.</param>
        private void DoSave(IKeePassEntry baseEntry)
        {
            if (baseEntry == null)
            {
                throw new ArgumentNullException(nameof(baseEntry));
            }

            if (!CanSave(baseEntry))
            {
                throw new InvalidOperationException("Cannot save in the current state.");
            }

            if (Original == null)
            {
                // New string...
                baseEntry.Fields.Add(WorkingCopy);
                Original    = WorkingCopy;
                WorkingCopy = Original.Clone();
            }
            else
            {
                // Existing string...
                if (Original.Key != WorkingCopy.Key)
                {
                    Original.Key = WorkingCopy.Key;
                }

                if (Original.Protected != WorkingCopy.Protected)
                {
                    Original.Protected = WorkingCopy.Protected;
                }

                if (Original.ClearValue != WorkingCopy.ClearValue)
                {
                    Original.ClearValue = WorkingCopy.ClearValue;
                }
            }
        }
예제 #17
0
            public IKeePassEntry AddEntry(IKeePassEntry entry)
            {
                var pwEntry = new PwEntry(true, true);

                if (!string.IsNullOrEmpty(entry.Title))
                {
                    pwEntry.Strings.Set(PwDefs.TitleField, new ProtectedString(true, entry.Title));
                }

                if (!string.IsNullOrEmpty(entry.UserName))
                {
                    pwEntry.Strings.Set(PwDefs.UserNameField, new ProtectedString(true, entry.UserName));
                }

                if (!string.IsNullOrEmpty(entry.Password))
                {
                    pwEntry.Strings.Set(PwDefs.PasswordField, new ProtectedString(true, entry.Password));
                }

                if (!string.IsNullOrEmpty(entry.Notes))
                {
                    pwEntry.Strings.Set(PwDefs.NotesField, new ProtectedString(true, entry.Notes));
                }

                if (!string.IsNullOrEmpty(entry.Url))
                {
                    pwEntry.Strings.Set(PwDefs.UrlField, new ProtectedString(true, entry.Url));
                }

                _group.AddEntry(pwEntry, true);
                Database.Modified = true;

                var wrapped = new KbdxEntry(pwEntry, Database, this);

                Entries.Add(wrapped);

                return(wrapped);
            }
예제 #18
0
        public void Initialize()
        {
            IRandomNumberGenerator rng = new Salsa20(new byte[32]);

            IResourceProvider resourceProvider = new MockResourceProvider();

            this.parentEntry = new MockEntry();
            this.parentEntry.Fields.Add(
                new KdbxString(ExistingFieldKey, ExistingFieldValue, rng, ExistingFieldProtected)
                );

            MethodInfo testMethod = GetType().GetRuntimeMethod(
                TestContext.TestName, new Type[0]
                );

            DetailsForAttribute specAttr = testMethod.GetCustomAttribute <DetailsForAttribute>();

            if (specAttr == null)
            {
                return;
            }
            else
            {
                if (specAttr.IsNew)
                {
                    this.viewModel = new FieldEditorViewModel(rng, resourceProvider);
                }
                else
                {
                    this.originalString = new KdbxString(EditedFieldKey, EditedFieldValue, rng, EditedFieldProtected);
                    this.parentEntry.Fields.Add(this.originalString);

                    this.viewModel = new FieldEditorViewModel(this.originalString, resourceProvider);
                }
            }
        }
예제 #19
0
 /// <summary>
 /// Creates an instance with the specified type of copy operation.
 /// </summary>
 /// <param name="type">The type of copy being performed (name vs password).</param>
 public CopyRequestedEventArgs(IKeePassEntry entry, ClipboardOperationType type)
 {
     Entry    = entry;
     CopyType = type;
 }
예제 #20
0
        private static string ResolvePlaceholder(string token, IKeePassEntry entry)
        {
            if (token == null)
            {
                throw new ArgumentNullException(nameof(token));
            }

            string placeholder = token;
            string specifier   = null;

            int firstColon = token.IndexOf(':');

            if (firstColon >= 0)
            {
                placeholder = token.Substring(0, firstColon);
                specifier   = token.Substring(firstColon + 1);
            }

            if (specifier == null)
            {
                if (placeholder.Equals("TITLE", StringComparison.OrdinalIgnoreCase))
                {
                    return(entry.Title.ClearValue);
                }
                else if (placeholder.Equals("USERNAME", StringComparison.OrdinalIgnoreCase))
                {
                    return(entry.UserName.ClearValue);
                }
                else if (placeholder.Equals("URL", StringComparison.OrdinalIgnoreCase))
                {
                    return(entry.Url.ClearValue);
                }
                else if (placeholder.Equals("PASSWORD", StringComparison.OrdinalIgnoreCase))
                {
                    return(entry.Password.ClearValue);
                }
                else if (placeholder.Equals("NOTES", StringComparison.OrdinalIgnoreCase))
                {
                    return(entry.Notes.ClearValue);
                }
                else if (placeholder.Equals("GROUP", StringComparison.OrdinalIgnoreCase))
                {
                    return(entry.Parent?.Title.ClearValue);
                }
                else if (placeholder.Equals("GROUP_NOTES", StringComparison.OrdinalIgnoreCase))
                {
                    return(entry.Parent?.Notes.ClearValue);
                }
                else if (placeholder.Equals("ENV_DIRSEP", StringComparison.OrdinalIgnoreCase))
                {
                    return(Path.DirectorySeparatorChar.ToString());
                }
                else if (placeholder.Equals("DT_SIMPLE", StringComparison.OrdinalIgnoreCase))
                {
                    return(DateTime.Now.ToString("yyyyMMddHHmmss"));
                }
                else if (placeholder.Equals("DT_YEAR", StringComparison.OrdinalIgnoreCase))
                {
                    return(DateTime.Now.ToString("yyyy"));
                }
                else if (placeholder.Equals("DT_MONTH", StringComparison.OrdinalIgnoreCase))
                {
                    return(DateTime.Now.ToString("MM"));
                }
                else if (placeholder.Equals("DT_DAY", StringComparison.OrdinalIgnoreCase))
                {
                    return(DateTime.Now.ToString("dd"));
                }
                else if (placeholder.Equals("DT_HOUR", StringComparison.OrdinalIgnoreCase))
                {
                    return(DateTime.Now.ToString("HH"));
                }
                else if (placeholder.Equals("DT_MINUTE", StringComparison.OrdinalIgnoreCase))
                {
                    return(DateTime.Now.ToString("mm"));
                }
                else if (placeholder.Equals("DT_SECOND", StringComparison.OrdinalIgnoreCase))
                {
                    return(DateTime.Now.ToString("ss"));
                }
                else if (placeholder.Equals("DT_UTC_SIMPLE", StringComparison.OrdinalIgnoreCase))
                {
                    return(DateTime.UtcNow.ToString("yyyyMMddHHmmss"));
                }
                else if (placeholder.Equals("DT_UTC_YEAR", StringComparison.OrdinalIgnoreCase))
                {
                    return(DateTime.UtcNow.ToString("yyyy"));
                }
                else if (placeholder.Equals("DT_UTC_MONTH", StringComparison.OrdinalIgnoreCase))
                {
                    return(DateTime.UtcNow.ToString("MM"));
                }
                else if (placeholder.Equals("DT_UTC_DAY", StringComparison.OrdinalIgnoreCase))
                {
                    return(DateTime.UtcNow.ToString("dd"));
                }
                else if (placeholder.Equals("DT_UTC_HOUR", StringComparison.OrdinalIgnoreCase))
                {
                    return(DateTime.UtcNow.ToString("HH"));
                }
                else if (placeholder.Equals("DT_UTC_MINUTE", StringComparison.OrdinalIgnoreCase))
                {
                    return(DateTime.UtcNow.ToString("mm"));
                }
                else if (placeholder.Equals("DT_UTC_SECOND", StringComparison.OrdinalIgnoreCase))
                {
                    return(DateTime.UtcNow.ToString("ss"));
                }
            }
            else
            {
                if (placeholder.Equals("URL", StringComparison.OrdinalIgnoreCase))
                {
                    return(GetUrlComponent(entry.Url.ClearValue, specifier));
                }
                else if (placeholder.Equals("C", StringComparison.OrdinalIgnoreCase))
                {
                    // C:foo is a comment
                    return(string.Empty);
                }
            }

            // Not a valid placeholder
            return(null);
        }
예제 #21
0
 public void SyncTo(IKeePassEntry template, bool updateModificationTime = true)
 {
     throw new NotImplementedException();
 }
예제 #22
0
        /// <summary>
        /// Given a string, potentially with KeePass placeholders, and the entry the string
        /// is associated with, resolves all placeholders and returns a new string.
        /// </summary>
        /// <param name="input">The string to resolve.</param>
        /// <param name="entry">The entry associated with <paramref name="input"/>.</param>
        /// <returns>A new string with relevant placeholders replaced.</returns>
        public static string Resolve(string input, IKeePassEntry entry)
        {
            if (entry == null)
            {
                throw new ArgumentNullException(nameof(entry));
            }

            if (string.IsNullOrEmpty(input))
            {
                return(input);
            }

            StringBuilder builder = new StringBuilder(input.Length);

            int len = input.Length;

            for (int i = 0; i < len; i++)
            {
                // If we are starting a new placeholder, look ahead in the string for
                // the end of it.
                // If we find it:
                // * Append the placeholder's value to 'builder'
                // * Move 'i' to the end of the token
                // If we don't:
                // * Append the "placeholder" token to 'builder'
                // * Move 'i' to the end of the string, we're done
                if (input[i] == PlaceholderOpen)
                {
                    if (i == len - 1)
                    {
                        builder.Append(PlaceholderOpen);
                    }
                    else
                    {
                        StringBuilder tokenBuilder = new StringBuilder();
                        for (int j = i + 1; j < len; j++)
                        {
                            if (input[j] == PlaceholderOpen)
                            {
                                // We hit a "nested" token - we discard the work so far and start over.
                                builder.Append(PlaceholderOpen);
                                builder.Append(tokenBuilder.ToString());
                                tokenBuilder.Clear();

                                // If this is the last character we'll never finish a token, so go ahead
                                // and get this over with because we're about to exit the loop.
                                if (j == len - 1)
                                {
                                    builder.Append(PlaceholderOpen);
                                    i = j;
                                }
                            }
                            else if (input[j] == PlaceholderClose)
                            {
                                string token    = tokenBuilder.ToString();
                                string replaced = ResolvePlaceholder(token, entry);
                                if (replaced == null)
                                {
                                    // If the token is not valid, we don't replace it.
                                    replaced = $"{PlaceholderOpen}{token}{PlaceholderClose}";
                                }

                                builder.Append(replaced);

                                // Move i forward now that we've evaluated it
                                i = j;
                                break;
                            }
                            else
                            {
                                tokenBuilder.Append(input[j]);
                                if (j == len - 1)
                                {
                                    // If we reached the end of the string, we don't have a
                                    // complete placeholder. Just append what we have and bail.
                                    builder.Append(PlaceholderOpen);
                                    builder.Append(tokenBuilder.ToString());
                                    i = j;
                                    break;
                                }
                            }
                        }
                    }
                }
                else
                {
                    // Otherwise we're not in a placeholder/token, so keep on trucking.
                    builder.Append(input[i]);
                }
            }

            return(builder.ToString());
        }
예제 #23
0
 public IEntryDetailsViewModel GetEntryDetailsViewModel(IKeePassEntry entry, bool editing)
 {
     throw new NotImplementedException();
 }