This is just documentation and the config/indexopts interfaces, actual
implementation will be added in following commits.
---
 bindings/python-cffi/notmuch2/_build.py    |  5 +++
 bindings/python-cffi/notmuch2/_database.py | 20 ++++++++++++
 doc/man1/notmuch-config.rst                | 23 +++++++++++++
 lib/config.cc                              |  3 ++
 lib/indexopts.c                            | 38 ++++++++++++++++++++++
 lib/notmuch.h                              | 23 +++++++++++++
 test/T590-libconfig.sh                     |  5 +++
 7 files changed, 117 insertions(+)

diff --git a/bindings/python-cffi/notmuch2/_build.py 
b/bindings/python-cffi/notmuch2/_build.py
index 2f3152c6..f918f96f 100644
--- a/bindings/python-cffi/notmuch2/_build.py
+++ b/bindings/python-cffi/notmuch2/_build.py
@@ -328,6 +328,11 @@ ffibuilder.cdef(
                                           notmuch_decryption_policy_t 
decrypt_policy);
     notmuch_decryption_policy_t
     notmuch_indexopts_get_decrypt_policy (const notmuch_indexopts_t 
*indexopts);
+    notmuch_status_t
+    notmuch_indexopts_set_filter (notmuch_indexopts_t *indexopts,
+                                  const char *filter_cmd);
+    const char *
+    notmuch_indexopts_get_filter (const notmuch_indexopts_t *indexopts);
     void
     notmuch_indexopts_destroy (notmuch_indexopts_t *options);
 
diff --git a/bindings/python-cffi/notmuch2/_database.py 
b/bindings/python-cffi/notmuch2/_database.py
index ba389a42..e547da08 100644
--- a/bindings/python-cffi/notmuch2/_database.py
+++ b/bindings/python-cffi/notmuch2/_database.py
@@ -878,3 +878,23 @@ class IndexOptions(base.NotmuchObject):
             self._opts_p, val.value)
         if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
             raise errors.NotmuchError(ret)
+
+    @property
+    def filter_cmd(self):
+        """Filtering program to extract text from non-text MIME parts.
+
+        CAUTION: improper use of this option may lead to remote code
+        execution on the user's machine. See the `index.filter` section
+        in :any:`notmuch-config(1)` for details. Make sure you read and
+        understand it before setting this property.
+
+        The value is a string executed as a shell command.
+        """
+        raw = capi.lib.notmuch_indexopts_get_filter(self._opts_p)
+        return base.BinString.from_cffi(raw)
+
+    @filter_cmd.setter
+    def filter_cmd(self, val):
+        ret = capi.lib.notmuch_indexopts_set_filter(self._opts_p, val)
+        if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
+            raise errors.NotmuchError(ret)
diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst
index 5106655f..38cbe289 100644
--- a/doc/man1/notmuch-config.rst
+++ b/doc/man1/notmuch-config.rst
@@ -132,6 +132,29 @@ paths are presumed relative to `$HOME` for items in section
 
    History: This configuration value was introduced in notmuch 0.38.
 
+.. nmconfig:: index.filter
+
+   Filtering program to convert non-text MIME parts to a text
+   representation for indexing. Will only be applied to those parts that
+   match ``index.as_text``.
+
+   CAUTION: It is very common for hostile actors to send emails with
+   crafted attachments that exploit bugs in common parsing libraries. It
+   is thus IMPERATIVE that your filtering program uses some sort of a
+   sandboxing mechanism, so that it cannot be subverted to attack your
+   system or steal your data.
+
+   The filter is a shell program (passed to ``SHELL`` or ``/bin/sh`` as
+   the argument of the ``-c`` option). The payload of the MIME part to
+   be filtered will be supplied on its `stdin`, it is expected to write
+   the text output to its `stdout`. The following environment variables
+   will be set:
+
+   * NOTMUCH_FILTER_MIME_TYPE - the ``type/subtype`` part of the
+     "content-type" header
+
+   History: This configuration value was introduced in notmuch 0.40.
+
 .. nmconfig:: index.decrypt
 
     Policy for decrypting encrypted messages during indexing.  Must be
diff --git a/lib/config.cc b/lib/config.cc
index 8fbd7336..43551ffe 100644
--- a/lib/config.cc
+++ b/lib/config.cc
@@ -608,6 +608,8 @@ _notmuch_config_key_to_string (notmuch_config_key_t key)
        return "search.authors_matched_separator";
     case NOTMUCH_CONFIG_INDEX_AS_TEXT:
        return "index.as_text";
+    case NOTMUCH_CONFIG_INDEX_FILTER:
+       return "index.filter";
     default:
        return NULL;
     }
@@ -664,6 +666,7 @@ _notmuch_config_default (notmuch_database_t *notmuch, 
notmuch_config_key_t key)
     case NOTMUCH_CONFIG_HOOK_DIR:
     case NOTMUCH_CONFIG_BACKUP_DIR:
     case NOTMUCH_CONFIG_OTHER_EMAIL:
+    case NOTMUCH_CONFIG_INDEX_FILTER:
        return NULL;
     default:
     case NOTMUCH_CONFIG_LAST:
diff --git a/lib/indexopts.c b/lib/indexopts.c
index 2ffd1942..6ced1181 100644
--- a/lib/indexopts.c
+++ b/lib/indexopts.c
@@ -22,6 +22,8 @@
 
 struct _notmuch_indexopts {
     _notmuch_crypto_t crypto;
+
+    char *filter_cmd;
 };
 
 notmuch_indexopts_t *
@@ -53,7 +55,26 @@ notmuch_database_get_default_indexopts (notmuch_database_t 
*db)
     }
 
     free (decrypt_policy);
+
+    char *filter_cmd;
+
+    err = notmuch_database_get_config (db, "index.filter", &filter_cmd);
+    if (err)
+       goto FAIL;
+
+    if (filter_cmd && *filter_cmd) {
+       ret->filter_cmd = talloc_strdup (ret, filter_cmd);
+       free (filter_cmd);
+       if (!ret->filter_cmd)
+           goto FAIL;
+    } else
+       free (filter_cmd);
+
     return ret;
+
+FAIL:
+    talloc_free (ret);
+    return NULL;
 }
 
 notmuch_status_t
@@ -74,6 +95,23 @@ notmuch_indexopts_get_decrypt_policy (const 
notmuch_indexopts_t *indexopts)
     return indexopts->crypto.decrypt;
 }
 
+notmuch_status_t
+notmuch_indexopts_set_filter (notmuch_indexopts_t *indexopts,
+                             const char *filter_cmd)
+{
+    talloc_free (indexopts->filter_cmd);
+    indexopts->filter_cmd = talloc_strdup (indexopts, filter_cmd);
+    if (!indexopts->filter_cmd)
+       return NOTMUCH_STATUS_OUT_OF_MEMORY;
+    return NOTMUCH_STATUS_SUCCESS;
+}
+
+const char *
+notmuch_indexopts_get_filter (const notmuch_indexopts_t *indexopts)
+{
+    return indexopts ? indexopts->filter_cmd : NULL;
+}
+
 void
 notmuch_indexopts_destroy (notmuch_indexopts_t *indexopts)
 {
diff --git a/lib/notmuch.h b/lib/notmuch.h
index 95918fc2..dfbbcb0c 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -2635,6 +2635,7 @@ typedef enum {
     NOTMUCH_CONFIG_INDEX_AS_TEXT,
     NOTMUCH_CONFIG_AUTHORS_SEPARATOR,
     NOTMUCH_CONFIG_AUTHORS_MATCHED_SEPARATOR,
+    NOTMUCH_CONFIG_INDEX_FILTER,
     NOTMUCH_CONFIG_LAST
 } notmuch_config_key_t;
 
@@ -2922,6 +2923,28 @@ notmuch_indexopts_get_decrypt_policy (const 
notmuch_indexopts_t *indexopts);
  *
  * @since libnotmuch 5.1 (notmuch 0.26)
  */
+
+/**
+ * Set a filtering program to extract text from non-text MIME parts.
+ *
+ * CAUTION: improper use of this option may lead to remote code
+ * execution on the user's machine. See the `index.filter` section in
+ * `notmuch-config(1)` for details. Make sure you read and understand
+ * it before calling this function.
+ */
+notmuch_status_t
+notmuch_indexopts_set_filter (notmuch_indexopts_t *indexopts,
+                             const char *filter_cmd);
+
+/**
+ * Return currently configured filtering program, or NULL if one is not
+ * configured.
+ *
+ * see notmuch_indexopts_set_filter
+ */
+const char *
+notmuch_indexopts_get_filter (const notmuch_indexopts_t *indexopts);
+
 void
 notmuch_indexopts_destroy (notmuch_indexopts_t *options);
 
diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh
index 9b364895..aa326dfb 100755
--- a/test/T590-libconfig.sh
+++ b/test/T590-libconfig.sh
@@ -443,6 +443,7 @@ cat <<'EOF' >EXPECTED
 13: ''
 14: ', '
 15: '| '
+16: 'NULL'
 == stderr ==
 EOF
 unset MAILDIR
@@ -731,6 +732,7 @@ notmuch config set search.authors_matched_separator "| "
 notmuch config set search.authors_separator ", "
 notmuch config set new.ignore "sekrit_junk"
 notmuch config set index.as_text "text/"
+notmuch config set index.filter "filter"
 cat c_head2 - c_tail <<'EOF' | test_C ${MAIL_DIR} %NULL% %NULL%
 {
     notmuch_config_key_t key;
@@ -760,6 +762,7 @@ cat <<'EOF' >EXPECTED
 13: 'text/'
 14: ', '
 15: '| '
+16: 'filter'
 == stderr ==
 EOF
 test_expect_equal_file EXPECTED OUTPUT
@@ -797,6 +800,7 @@ cat <<'EOF' >EXPECTED
 13: ''
 14: ', '
 15: '| '
+16: 'NULL'
 == stderr ==
 EOF
 test_expect_equal_file EXPECTED OUTPUT.clean
@@ -869,6 +873,7 @@ database.hook_dir MAIL_DIR/.notmuch/hooks
 database.mail_root MAIL_DIR
 database.path MAIL_DIR
 index.as_text text/
+index.filter filter
 key with spaces value, with, spaces!
 maildir.synchronize_flags true
 new.ignore sekrit_junk
-- 
2.47.3

_______________________________________________
notmuch mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to