diff --git a/.gitignore b/.gitignore index 73cf0b10..10fde9b5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ dist/ *.egg-info/ build/ *.xml +Pipfile Pipfile.lock *.kdbx *.kdbx.out +.idea diff --git a/README.rst b/README.rst index bcd156fd..bcdce39b 100644 --- a/README.rst +++ b/README.rst @@ -66,7 +66,7 @@ Finding Entries **find_entries** (title=None, username=None, password=None, url=None, notes=None, otp=None, path=None, uuid=None, tags=None, string=None, group=None, recursive=True, regex=False, flags=None, history=False, first=False) -Returns entries which match all provided parameters, where ``title``, ``username``, ``password``, ``url``, ``notes``, ``otp``, and ``autotype_sequence`` are strings, ``path`` is a list, ``string`` is a dict, ``autotype_enabled`` is a boolean, ``uuid`` is a ``uuid.UUID`` and ``tags`` is a list of strings. This function has optional ``regex`` boolean and ``flags`` string arguments, which means to interpret search strings as `XSLT style`_ regular expressions with `flags`_. +Returns entries which match all provided parameters, where ``title``, ``username``, ``password``, ``url``, ``notes``, ``otp``, ``autotype_window`` and ``autotype_sequence`` are strings, ``path`` is a list, ``string`` is a dict, ``autotype_enabled`` is a boolean, ``uuid`` is a ``uuid.UUID`` and ``tags`` is a list of strings. This function has optional ``regex`` boolean and ``flags`` string arguments, which means to interpret search strings as `XSLT style`_ regular expressions with `flags`_. .. _XSLT style: https://www.xml.com/pub/a/2003/06/04/tr.html .. _flags: https://www.w3.org/TR/xpath-functions/#flags diff --git a/pykeepass/entry.py b/pykeepass/entry.py index 2ed59c5a..51ebc886 100644 --- a/pykeepass/entry.py +++ b/pykeepass/entry.py @@ -26,7 +26,7 @@ class Entry(BaseElement): def __init__(self, title=None, username=None, password=None, url=None, notes=None, otp=None, tags=None, expires=False, expiry_time=None, - icon=None, autotype_sequence=None, autotype_enabled=True, + icon=None, autotype_sequence=None, autotype_enabled=True, autotype_window=None, element=None, kp=None): self._kp = kp @@ -60,7 +60,11 @@ def __init__(self, title=None, username=None, password=None, url=None, E.AutoType( E.Enabled(str(autotype_enabled)), E.DataTransferObfuscation('0'), - E.DefaultSequence(str(autotype_sequence) if autotype_sequence else '') + E.DefaultSequence(str(autotype_sequence) if autotype_sequence else ''), + E.Association( + E.Window(str(autotype_window) if autotype_window else ''), + E.KeystrokeSequence('') + ) ) ) # FIXME: include custom_properties in constructor @@ -268,6 +272,18 @@ def autotype_sequence(self): def autotype_sequence(self, value): self._element.find('AutoType/DefaultSequence').text = value + @property + def autotype_window(self): + """str: get or set [autotype target window filter](https://keepass.info/help/base/autotype.html#autowindows)""" + sequence = self._element.find('AutoType/Association/Window') + if sequence is None or sequence.text == '': + return None + return sequence.text + + @autotype_window.setter + def autotype_window(self, value): + self._element.find('AutoType/Association/Window').text = value + @property def is_a_history_entry(self): """bool: check if entry is History entry""" diff --git a/pykeepass/xpath.py b/pykeepass/xpath.py index c4e6fea5..3769f338 100644 --- a/pykeepass/xpath.py +++ b/pykeepass/xpath.py @@ -31,6 +31,7 @@ 'tags': '/Tags[text()="{}"]/..', 'string': '/String/Key[text()="{}"]/../Value[text()="{}"]/../..', 'autotype_sequence': '/AutoType/DefaultSequence[text()="{}"]/../..', + 'autotype_window': '/AutoType/Association/Window[text()="{}"]/../../..', 'autotype_enabled': '/AutoType/Enabled[text()="{}"]/../..', 'otp': '/String/Key[text()="otp"]/../Value[text()="{}"]/../..', }, @@ -44,6 +45,7 @@ 'tags': '/Tags[re:test(text(), "{}", "{flags}")]/..', 'string': '/String/Key[text()="{}"]/../Value[re:test(text(), "{}", "{flags}")]/../..', 'autotype_sequence': '/AutoType/DefaultSequence[re:test(text(), "{}", "{flags}")]/../..', + 'autotype_window': '/AutoType/Association/Window[re:test(text(), "{}", "{flags}")]/../../..', 'autotype_enabled': '/AutoType/Enabled[re:test(text(), "{}", "{flags}")]/../..', 'otp': '/String/Key[text()="otp"]/../Value[re:test(text(), "{}", "{flags}")]/../..', } diff --git a/tests/test3.kdbx b/tests/test3.kdbx index ab02bbf7..e61a2299 100644 Binary files a/tests/test3.kdbx and b/tests/test3.kdbx differ diff --git a/tests/tests.py b/tests/tests.py index 4c574bae..99f56dd3 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -8,7 +8,6 @@ from datetime import datetime, timedelta from dateutil import tz -from lxml.etree import Element from pathlib import Path from io import BytesIO @@ -16,7 +15,6 @@ from pykeepass import PyKeePass, icons from pykeepass.entry import Entry from pykeepass.group import Group -from pykeepass.kdbx_parsing import KDBX from pykeepass.exceptions import BinaryError, CredentialsError, HeaderChecksumError """ @@ -144,6 +142,11 @@ def test_find_entries_by_autotype_sequence(self): self.assertEqual(len(results), 1) self.assertEqual(results[0].autotype_sequence, '{USERNAME}{TAB}{PASSWORD}{ENTER}') + def test_find_entries_by_autotype_window(self): + results = self.kp.find_entries(autotype_window='test', regex=True, flags="i") + self.assertEqual(len(results), 1) + self.assertEqual(results[0].autotype_window, 'TEST') + def test_find_entries_by_autotype_enabled(self): results = self.kp.find_entries(autotype_enabled=True) self.assertEqual(len(results), len(self.kp.entries) - 1)