[PATCH v5] python: add decrypt_policy argument to Database.index_file()

Daniel Kahn Gillmor dkg at fifthhorseman.net
Tue Dec 19 11:08:50 PST 2017


We adopt a pythonic idiom here with an optional argument, rather than
exposing the user to the C indexopts object directly.

This now includes a simple test to ensure that the decrypt_policy
argument works as expected.
---
 bindings/python/notmuch/database.py | 45 +++++++++++++++++++++++++++++++++++--
 bindings/python/notmuch/globals.py  |  5 +++++
 test/T390-python.sh                 | 39 ++++++++++++++++++++++++++++++++
 3 files changed, 87 insertions(+), 2 deletions(-)

diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py
index fe09b330..a1ae14fc 100644
--- a/bindings/python/notmuch/database.py
+++ b/bindings/python/notmuch/database.py
@@ -29,6 +29,7 @@ from .globals import (
     NotmuchConfigListP,
     NotmuchDatabaseP,
     NotmuchDirectoryP,
+    NotmuchIndexoptsP,
     NotmuchMessageP,
     NotmuchTagsP,
 )
@@ -73,6 +74,9 @@ class Database(object):
     MODE = Enum(['READ_ONLY', 'READ_WRITE'])
     """Constants: Mode in which to open the database"""
 
+    DECRYPTION_POLICY = Enum(['FALSE', 'TRUE', 'AUTO', 'NOSTASH'])
+    """Constants: policies for decrypting messages during indexing"""
+
     """notmuch_database_get_directory"""
     _get_directory = nmlib.notmuch_database_get_directory
     _get_directory.argtypes = [NotmuchDatabaseP, c_char_p, POINTER(NotmuchDirectoryP)]
@@ -401,13 +405,25 @@ class Database(object):
         # return the Directory, init it with the absolute path
         return Directory(abs_dirpath, dir_p, self)
 
+    _get_default_indexopts = nmlib.notmuch_database_get_default_indexopts
+    _get_default_indexopts.argtypes = [NotmuchDatabaseP]
+    _get_default_indexopts.restype = NotmuchIndexoptsP
+
+    _indexopts_set_decrypt_policy = nmlib.notmuch_indexopts_set_decrypt_policy
+    _indexopts_set_decrypt_policy.argtypes = [NotmuchIndexoptsP, c_uint]
+    _indexopts_set_decrypt_policy.restype = None
+
+    _indexopts_destroy = nmlib.notmuch_indexopts_destroy
+    _indexopts_destroy.argtypes = [NotmuchIndexoptsP]
+    _indexopts_destroy.restype = None
+
     _index_file = nmlib.notmuch_database_index_file
     _index_file.argtypes = [NotmuchDatabaseP, c_char_p,
                              c_void_p,
                              POINTER(NotmuchMessageP)]
     _index_file.restype = c_uint
 
-    def index_file(self, filename, sync_maildir_flags=False):
+    def index_file(self, filename, sync_maildir_flags=False, decrypt_policy=None):
         """Adds a new message to the database
 
         :param filename: should be a path relative to the path of the
@@ -428,6 +444,23 @@ class Database(object):
             API. You might want to look into the underlying method
             :meth:`Message.maildir_flags_to_tags`.
 
+        :param decrypt_policy: If the message contains any encrypted
+            parts, and decrypt_policy is set to
+            :attr:`DECRYPTION_POLICY`.TRUE, notmuch will try to
+            decrypt the message and index the cleartext, stashing any
+            discovered session keys.  If it is set to
+            :attr:`DECRYPTION_POLICY`.FALSE, it will never try to
+            decrypt during indexing.  If it is set to
+            :attr:`DECRYPTION_POLICY`.AUTO, then it will try to use
+            any stashed session keys it knows about, but will not try
+            to access the user's secret keys.
+            :attr:`DECRYPTION_POLICY`.NOSTASH behaves the same as
+            :attr:`DECRYPTION_POLICY`.TRUE except that no session keys
+            are stashed in the database.  If decrypt_policy is set to
+            None (the default), then the database itself will decide
+            whether to decrypt, based on the `index.decrypt`
+            configuration setting (see notmuch-config(1)).
+
         :returns: On success, we return
 
            1) a :class:`Message` object that can be used for things
@@ -458,7 +491,15 @@ class Database(object):
         """
         self._assert_db_is_initialized()
         msg_p = NotmuchMessageP()
-        status = self._index_file(self._db, _str(filename), c_void_p(None), byref(msg_p))
+        indexopts = c_void_p(None)
+        if decrypt_policy is not None:
+            indexopts = self._get_default_indexopts(self._db)
+            self._indexopts_set_decrypt_policy(indexopts, decrypt_policy)
+
+        status = self._index_file(self._db, _str(filename), indexopts, byref(msg_p))
+
+        if indexopts:
+            self._indexopts_destroy(indexopts)
 
         if not status in [STATUS.SUCCESS, STATUS.DUPLICATE_MESSAGE_ID]:
             raise NotmuchError(status)
diff --git a/bindings/python/notmuch/globals.py b/bindings/python/notmuch/globals.py
index b33e10d3..97413996 100644
--- a/bindings/python/notmuch/globals.py
+++ b/bindings/python/notmuch/globals.py
@@ -93,3 +93,8 @@ NotmuchFilenamesP = POINTER(NotmuchFilenamesS)
 class NotmuchConfigListS(Structure):
     pass
 NotmuchConfigListP = POINTER(NotmuchConfigListS)
+
+
+class NotmuchIndexoptsS(Structure):
+    pass
+NotmuchIndexoptsP = POINTER(NotmuchIndexoptsS)
diff --git a/test/T390-python.sh b/test/T390-python.sh
index 312d61e8..9f71ce3c 100755
--- a/test/T390-python.sh
+++ b/test/T390-python.sh
@@ -5,6 +5,7 @@ test_description="python bindings"
 test_require_external_prereq ${NOTMUCH_PYTHON}
 
 add_email_corpus
+add_gnupg_home
 
 test_begin_subtest "compare thread ids"
 test_python <<EOF
@@ -155,4 +156,42 @@ print(list(v) == [])
 EOF
 test_expect_equal "$(cat OUTPUT)" "True"
 
+mkdir -p "${MAIL_DIR}/cur"
+fname="${MAIL_DIR}/cur/simplemsg.eml"
+cat <<EOF > "$fname"
+From: test_suite at notmuchmail.org
+To: test_suite at notmuchmail.org
+Subject: encrypted message
+Date: Sat, 01 Jan 2000 12:00:00 +0000
+Message-ID: <simplemsg at crypto.notmuchmail.org>
+MIME-Version: 1.0
+Content-Type: multipart/encrypted; boundary="=-=-=";
+	protocol="application/pgp-encrypted"
+
+--=-=-=
+Content-Type: application/pgp-encrypted
+
+Version: 1
+
+--=-=-=
+Content-Type: application/octet-stream
+
+$(printf 'Content-Type: text/plain\n\nThis is the sekrit message\n' | gpg --no-tty --batch --quiet --trust-model=always --encrypt --armor --recipient test_suite at notmuchmail.org)
+--=-=-=--
+EOF
+
+test_begin_subtest "index message with decryption"
+test_python <<EOF
+import notmuch
+db = notmuch.Database(mode=notmuch.Database.MODE.READ_WRITE)
+(m, status) = db.index_file('$fname', decrypt_policy=notmuch.Database.DECRYPTION_POLICY.TRUE)
+if status == notmuch.errors.STATUS.DUPLICATE_MESSAGE_ID:
+   print("got duplicate message")
+q_new = notmuch.Query(db, 'sekrit')
+for m in q_new.search_messages():
+    print(m.get_filename())
+EOF
+echo "$fname" > EXPECTED
+test_expect_equal_file EXPECTED OUTPUT
+
 test_done
-- 
2.15.1



More information about the notmuch mailing list