[PATCH] debian: Add packaging for python3-notmuch2

2019-12-23 Thread Daniel Kahn Gillmor
Ship a new debian package for the notmuch2 CFFI-based Python interface
to notmuch.

Unlike the notmuch python module, the new notmuch2 module is no longer
arch-independent, because it builds and ships a shared object in
addition to the python code.

This patch encourages new downstream development to rely on notmuch2
instead of on notmuch, to get the benefits of the new module.

I welcome any suggested improvements to this packaging, but it appears
to me to be sufficient to get "import notmuch2" to work and do some
basic tests.
---
 debian/control | 24 +++-
 debian/rules   | 11 ++-
 2 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/debian/control b/debian/control
index fb2b31c1..a1371fc8 100644
--- a/debian/control
+++ b/debian/control
@@ -98,6 +98,25 @@ Depends:
  libnotmuch5 (>= ${source:Version}),
  ${misc:Depends},
  ${python3:Depends},
+Description: Python 3 legacy interface to the notmuch mail search and index 
library
+ Notmuch is a system for indexing, searching, reading, and tagging
+ large collections of email messages in maildir or mh format. It uses
+ the Xapian library to provide fast, full-text search with a very
+ convenient search syntax.
+ .
+ This package provides a legacy Python 3 interface to the notmuch
+ functionality, directly interfacing with a shared notmuch library.
+ .
+ New projects are encouraged to use python3-notmuch2 instead.
+
+Package: python3-notmuch2
+Architecture: any
+Section: python
+Depends:
+ libnotmuch5 (>= ${source:Version}),
+ ${misc:Depends},
+ ${python3:Depends},
+ ${shlibs:Depends},
 Description: Python 3 interface to the notmuch mail search and index library
  Notmuch is a system for indexing, searching, reading, and tagging
  large collections of email messages in maildir or mh format. It uses
@@ -105,7 +124,10 @@ Description: Python 3 interface to the notmuch mail search 
and index library
  convenient search syntax.
  .
  This package provides a Python 3 interface to the notmuch
- functionality, directly interfacing with a shared notmuch library.
+ functionality using CFFI bindings, which interface with a shared
+ notmuch library.
+ .
+ This is the preferred way to use notmuch via Python.
 
 Package: ruby-notmuch
 Architecture: any
diff --git a/debian/rules b/debian/rules
index bf9d0bbd..8de49d0f 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,7 +1,5 @@
 #!/usr/bin/make -f
 
-export PYBUILD_NAME=notmuch
-
 export DEB_BUILD_MAINT_OPTIONS = hardening=+all
 
 %:
@@ -19,17 +17,20 @@ override_dh_auto_configure:
 
 override_dh_auto_build:
dh_auto_build -- V=1
-   dh_auto_build --buildsystem=pybuild --sourcedirectory bindings/python
+   PYBUILD_NAME=notmuch dh_auto_build --buildsystem=pybuild 
--sourcedirectory bindings/python
+   PYBUILD_NAME=notmuch2 dh_auto_build --buildsystem=pybuild 
--sourcedirectory bindings/python-cffi
$(MAKE) -C contrib/notmuch-mutt
 
 override_dh_auto_clean:
dh_auto_clean
-   dh_auto_clean --buildsystem=pybuild --sourcedirectory bindings/python
+   PYBUILD_NAME=notmuch dh_auto_clean --buildsystem=pybuild 
--sourcedirectory bindings/python
+   PYBUILD_NAME=notmuch2 dh_auto_clean --buildsystem=pybuild 
--sourcedirectory bindings/python-cffi
dh_auto_clean --sourcedirectory bindings/ruby
$(MAKE) -C contrib/notmuch-mutt clean
 
 override_dh_auto_install:
dh_auto_install
-   dh_auto_install --buildsystem=pybuild --sourcedirectory bindings/python
+   PYBUILD_NAME=notmuch dh_auto_install --buildsystem=pybuild 
--sourcedirectory bindings/python
+   PYBUILD_NAME=notmuch2 dh_auto_install --buildsystem=pybuild 
--sourcedirectory bindings/python-cffi
$(MAKE) -C contrib/notmuch-mutt DESTDIR=$(CURDIR)/debian/tmp install
dh_auto_install --sourcedirectory bindings/ruby
-- 
2.24.0

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] python/notmuch2: fix typo for "destroyed"

2019-12-23 Thread David Bremner
Daniel Kahn Gillmor  writes:

> Another fix to the docstrings, this time for the English part of the
> docstrings, not the Python class name.  No functional changes here.
>
> Signed-off-by: Daniel Kahn Gillmor 

pushed,

d
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH] python/notmuch2: fix typo for ObjectDestroyedError

2019-12-23 Thread David Bremner
Daniel Kahn Gillmor  writes:

> There is no functional change here, just a fix to a typo in the
> docstrings.
>
> Signed-off-by: Daniel Kahn Gillmor 
pushed.

d
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] debian: Override lintian suggestion to move elpa-notmuch to Section: lisp

2019-12-23 Thread Daniel Kahn Gillmor
Signed-off-by: Daniel Kahn Gillmor 
---
 debian/elpa-notmuch.lintian-overrides | 4 
 1 file changed, 4 insertions(+)
 create mode 100644 debian/elpa-notmuch.lintian-overrides

diff --git a/debian/elpa-notmuch.lintian-overrides 
b/debian/elpa-notmuch.lintian-overrides
new file mode 100644
index ..aa275eda
--- /dev/null
+++ b/debian/elpa-notmuch.lintian-overrides
@@ -0,0 +1,4 @@
+# elpa-notmuch is an elisp plugin for dealing with e-mail.  We can
+# already tell from the package name that it is an elisp package, so
+# it belongs in Section: mail, and lintian is being too strict here.
+elpa-notmuch: wrong-section-according-to-package-name elpa-notmuch => lisp
-- 
2.24.0

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] python/notmuch2: fix typo for "destroyed"

2019-12-23 Thread Daniel Kahn Gillmor
Another fix to the docstrings, this time for the English part of the
docstrings, not the Python class name.  No functional changes here.

Signed-off-by: Daniel Kahn Gillmor 
---
 bindings/python-cffi/notmuch2/_database.py | 28 +++---
 bindings/python-cffi/notmuch2/_errors.py   |  2 +-
 bindings/python-cffi/notmuch2/_message.py  | 24 +--
 bindings/python-cffi/notmuch2/_thread.py   | 20 
 4 files changed, 37 insertions(+), 37 deletions(-)

diff --git a/bindings/python-cffi/notmuch2/_database.py 
b/bindings/python-cffi/notmuch2/_database.py
index 7ef4fe17..95f59ca0 100644
--- a/bindings/python-cffi/notmuch2/_database.py
+++ b/bindings/python-cffi/notmuch2/_database.py
@@ -262,7 +262,7 @@ class Database(base.NotmuchObject):
 
 This is returned as a :class:`pathlib.Path` instance.
 
-:raises ObjectDestroyedError: if used after destoryed.
+:raises ObjectDestroyedError: if used after destroyed.
 """
 try:
 return self._cache_path
@@ -277,7 +277,7 @@ class Database(base.NotmuchObject):
 
 This is a positive integer.
 
-:raises ObjectDestroyedError: if used after destoryed.
+:raises ObjectDestroyedError: if used after destroyed.
 """
 try:
 return self._cache_version
@@ -296,7 +296,7 @@ class Database(base.NotmuchObject):
 
 A read-only database will never be upgradable.
 
-:raises ObjectDestroyedError: if used after destoryed.
+:raises ObjectDestroyedError: if used after destroyed.
 """
 ret = capi.lib.notmuch_database_needs_upgrade(self._db_p)
 return bool(ret)
@@ -320,7 +320,7 @@ class Database(base.NotmuchObject):
not imply durability, it only ensures the changes are
performed atomically.
 
-:raises ObjectDestroyedError: if used after destoryed.
+:raises ObjectDestroyedError: if used after destroyed.
 """
 ctx = AtomicContext(self, '_db_p')
 return ctx
@@ -330,7 +330,7 @@ class Database(base.NotmuchObject):
 
 Returned as a ``(revision, uuid)`` namedtuple.
 
-:raises ObjectDestroyedError: if used after destoryed.
+:raises ObjectDestroyedError: if used after destroyed.
 """
 raw_uuid = capi.ffi.new('char**')
 rev = capi.lib.notmuch_database_get_revision(self._db_p, raw_uuid)
@@ -387,7 +387,7 @@ class Database(base.NotmuchObject):
READ_ONLY mode.
 :raises UpgradeRequiredError: The database must be upgraded
first.
-:raises ObjectDestroyedError: if used after destoryed.
+:raises ObjectDestroyedError: if used after destroyed.
 """
 if not hasattr(os, 'PathLike') and isinstance(filename, pathlib.Path):
 filename = bytes(filename)
@@ -426,7 +426,7 @@ class Database(base.NotmuchObject):
READ_ONLY mode.
 :raises UpgradeRequiredError: The database must be upgraded
first.
-:raises ObjectDestroyedError: if used after destoryed.
+:raises ObjectDestroyedError: if used after destroyed.
 """
 if not hasattr(os, 'PathLike') and isinstance(filename, pathlib.Path):
 filename = bytes(filename)
@@ -458,7 +458,7 @@ class Database(base.NotmuchObject):
 :raises OutOfMemoryError: When there is no memory to allocate
the message instance.
 :raises XapianError: A Xapian exception ocurred.
-:raises ObjectDestroyedError: if used after destoryed.
+:raises ObjectDestroyedError: if used after destroyed.
 """
 msg_pp = capi.ffi.new('notmuch_message_t **')
 ret = capi.lib.notmuch_database_find_message(self._db_p,
@@ -489,7 +489,7 @@ class Database(base.NotmuchObject):
 :raises OutOfMemoryError: When there is no memory to allocate
the message instance.
 :raises XapianError: A Xapian exception ocurred.
-:raises ObjectDestroyedError: if used after destoryed.
+:raises ObjectDestroyedError: if used after destroyed.
 """
 if not hasattr(os, 'PathLike') and isinstance(filename, pathlib.Path):
 filename = bytes(filename)
@@ -522,7 +522,7 @@ class Database(base.NotmuchObject):
 
 :rtype: ImmutableTagSet
 
-:raises ObjectDestroyedError: if used after destoryed.
+:raises ObjectDestroyedError: if used after destroyed.
 """
 try:
 ref = self._cached_tagset
@@ -570,7 +570,7 @@ class Database(base.NotmuchObject):
 
 :raises OutOfMemoryError: if no memory is available to
allocate the query.
-:raises ObjectDestroyedError: if used after destoryed.
+:raises ObjectDestroyedError: if used after destroyed.
 """
 query = self._create_query(query,
omit_excluded=omit_excluded,
@@ -587,7 +587,7 @@ class Database(base.NotmuchObject):

[PATCH] python/notmuch2: fix typo for ObjectDestroyedError

2019-12-23 Thread Daniel Kahn Gillmor
There is no functional change here, just a fix to a typo in the
docstrings.

Signed-off-by: Daniel Kahn Gillmor 
---
 bindings/python-cffi/notmuch2/__init__.py  | 2 +-
 bindings/python-cffi/notmuch2/_database.py | 4 ++--
 bindings/python-cffi/notmuch2/_tags.py | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/bindings/python-cffi/notmuch2/__init__.py 
b/bindings/python-cffi/notmuch2/__init__.py
index 4d76ec15..613317e0 100644
--- a/bindings/python-cffi/notmuch2/__init__.py
+++ b/bindings/python-cffi/notmuch2/__init__.py
@@ -13,7 +13,7 @@ Errors
 All errors occuring due to errors from the underlying notmuch database
 are subclasses of the :exc:`NotmuchError`.  Due to memory management
 it is possible to try and use an object after it has been freed.  In
-this case a :exc:`ObjectDestoryedError` will be raised.
+this case a :exc:`ObjectDestroyedError` will be raised.
 
 Memory Management
 =
diff --git a/bindings/python-cffi/notmuch2/_database.py 
b/bindings/python-cffi/notmuch2/_database.py
index a1c624a7..7ef4fe17 100644
--- a/bindings/python-cffi/notmuch2/_database.py
+++ b/bindings/python-cffi/notmuch2/_database.py
@@ -342,7 +342,7 @@ class Database(base.NotmuchObject):
 def default_indexopts(self):
 """Returns default index options for the database.
 
-:raises ObjectDestoryedError: if used after destroyed.
+:raises ObjectDestroyedError: if used after destroyed.
 
 :returns: :class:`IndexOptions`.
 """
@@ -770,7 +770,7 @@ class IndexOptions(base.NotmuchObject):
 You can change this policy by assigning a new
 :class:`DecryptionPolicy` to this property.
 
-:raises ObjectDestoryedError: if used after destroyed.
+:raises ObjectDestroyedError: if used after destroyed.
 
 :returns: A :class:`DecryptionPolicy` enum instance.
 """
diff --git a/bindings/python-cffi/notmuch2/_tags.py 
b/bindings/python-cffi/notmuch2/_tags.py
index fe422a79..212852a8 100644
--- a/bindings/python-cffi/notmuch2/_tags.py
+++ b/bindings/python-cffi/notmuch2/_tags.py
@@ -277,7 +277,7 @@ class TagsIter(base.NotmuchObject, 
collections.abc.Iterator):
 :param errors: If using a codec, this is the error handler.
See :func:`str.decode` to which this is passed on.
 
-:raises ObjectDestoryedError: if used after destroyed.
+:raises ObjectDestroyedError: if used after destroyed.
 """
 _tags_p = base.MemoryPointer()
 
-- 
2.24.0

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH] debian: add Build-Depends-Package for libnotmuch5.symbols

2019-12-23 Thread Daniel Kahn Gillmor
See lintian informational tag
symbols-file-missing-build-depends-package-field for hints about this
minor metadata update.

Signed-off-by: Daniel Kahn Gillmor 
---
 debian/libnotmuch5.symbols | 1 +
 1 file changed, 1 insertion(+)

diff --git a/debian/libnotmuch5.symbols b/debian/libnotmuch5.symbols
index 308567b8..2ae73dad 100644
--- a/debian/libnotmuch5.symbols
+++ b/debian/libnotmuch5.symbols
@@ -1,4 +1,5 @@
 libnotmuch.so.5 libnotmuch5 #MINVER#
+* Build-Depends-Package: libnotmuch-dev
  notmuch_built_with@Base 0.23~rc0
  notmuch_config_list_destroy@Base 0.23~rc0
  notmuch_config_list_key@Base 0.23~rc0
-- 
2.24.0

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 1/2] legacy-display: accept text/plain legacy display parts

2019-12-23 Thread Daniel Kahn Gillmor
https://www.ietf.org/id/draft-autocrypt-lamps-protected-headers-02.html
Makes it clear that the "Legacy Display" part of an encrypted message
with protected headers can (and indeed, should) be of content-type
text/plain, though some clients still generate the Legacy Display part
as content-type text/rfc822-headers.  Notmuch should recognize the
part whichever of the two content-types it uses.

See also discussion in
https://github.com/autocrypt/protected-headers/issues/23 for why the
community of implementers is moving in the direction of text/plain.

Signed-off-by: Daniel Kahn Gillmor 
---
 util/repair.c | 6 --
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/util/repair.c b/util/repair.c
index 9fba97b7..4385d16f 100644
--- a/util/repair.c
+++ b/util/repair.c
@@ -49,8 +49,10 @@ _notmuch_crypto_payload_has_legacy_display (GMimeObject 
*payload)
 if (g_mime_multipart_get_count (mpayload) != 2)
return false;
 first = g_mime_multipart_get_part (mpayload, 0);
-if (! g_mime_content_type_is_type (g_mime_object_get_content_type (first),
-  "text", "rfc822-headers"))
+if (! (g_mime_content_type_is_type (g_mime_object_get_content_type (first),
+   "text", "plain") ||
+  g_mime_content_type_is_type (g_mime_object_get_content_type (first),
+   "text", "rfc822-headers")))
return false;
 protected_header_parameter = g_mime_object_get_content_type_parameter 
(first, "protected-headers");
 if ((! protected_header_parameter) || strcmp (protected_header_parameter, 
"v1"))
-- 
2.24.0

___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


[PATCH 2/2] legacy-display: drop tests that try to match headers in a Legacy Display part

2019-12-23 Thread Daniel Kahn Gillmor
These tests were an attempt to establish that the content of the
"Legacy Display" part is the same as the actual protected headers of
the message.  But this is more conservative than we need to be.

https://www.ietf.org/id/draft-autocrypt-lamps-protected-headers-02.html
section 5.3 makes clear that the Legacy Display part is purely
decorative, and section 5.2.1 clarifies that the detection can be done
purely by MIME structure and Content-Type alone.

Furthermore, now that we're accepting text/plain Legacy Display parts,
it's not clear the lines in the Legacy Display part should be
interpreted as needing an exact string match (e.g. "real" headers are
likely to be RFC 2047 encoded, but the text/plain Legacy Display part
probably should not be).

The concerns that motivated this test in the past were twofold: that
we might accidentally hide some information from the reader of the
message that they should have available to them, or that we could
introduce a covert channel that would be invisible to other clients.

I no longer think these are significant concerns:

 a) There will be no accidental misidentification of a Legacy Display
part.  The identification of the Legacy Display part is
unambiguous due to MIME structure and Content-Type.  MIME
structure MUST be the first child part of a two-part
multipart/mixed Cryptographic Payload. And the
protected-headers=v1 content-type parameter must be present on
both the cryptographic payload and the legacy display part, so no
one would accidentally generate this structure and have it be
accidentally matched.

 b) As for creating a covert channel, many such channels already
exist.  For example, non-standard e-mail headers, custom MIME
types, unusual MIME structures, etc, all make it possible to ship
some content in a message that will be visible in some MUAs but
not in others.  This doesn't make the situation demonstrably
worse.

Signed-off-by: Daniel Kahn Gillmor 
---
 util/repair.c | 60 ++-
 1 file changed, 2 insertions(+), 58 deletions(-)

diff --git a/util/repair.c b/util/repair.c
index 4385d16f..6c13601d 100644
--- a/util/repair.c
+++ b/util/repair.c
@@ -27,13 +27,7 @@ _notmuch_crypto_payload_has_legacy_display (GMimeObject 
*payload)
 {
 GMimeMultipart *mpayload;
 const char *protected_header_parameter;
-GMimeTextPart *legacy_display;
-char *legacy_display_header_text = NULL;
-GMimeStream *stream = NULL;
-GMimeParser *parser = NULL;
-GMimeObject *legacy_header_object = NULL, *first;
-GMimeHeaderList *legacy_display_headers = NULL, *protected_headers = NULL;
-bool ret = false;
+GMimeObject *first;
 
 if (! g_mime_content_type_is_type (g_mime_object_get_content_type 
(payload),
   "multipart", "mixed"))
@@ -60,57 +54,7 @@ _notmuch_crypto_payload_has_legacy_display (GMimeObject 
*payload)
 if (! GMIME_IS_TEXT_PART (first))
return false;
 
-/* ensure that the headers in the first part all match the values
- * found in the payload's own protected headers!  if they don't,
- * we should not treat this as a valid "legacy-display" part.
- *
- * Crafting a GMimeHeaderList object from the content of the
- * text/rfc822-headers part is pretty clumsy; we should probably
- * push something into GMime that makes this a one-shot
- * operation. */
-if ((protected_headers = g_mime_object_get_header_list (payload), 
protected_headers) &&
-   (legacy_display = GMIME_TEXT_PART (first), legacy_display) &&
-   (legacy_display_header_text = g_mime_text_part_get_text 
(legacy_display), legacy_display_header_text) &&
-   (stream = g_mime_stream_mem_new_with_buffer 
(legacy_display_header_text, strlen (legacy_display_header_text)), stream) &&
-   (g_mime_stream_write (stream, "\r\n\r\n", 4) == 4) &&
-   (g_mime_stream_seek (stream, 0, GMIME_STREAM_SEEK_SET) == 0) &&
-   (parser = g_mime_parser_new_with_stream (stream), parser) &&
-   (legacy_header_object = g_mime_parser_construct_part (parser, NULL), 
legacy_header_object) &&
-   (legacy_display_headers = g_mime_object_get_header_list 
(legacy_header_object), legacy_display_headers)) {
-   /* walk through legacy_display_headers, comparing them against
-* their values in the protected_headers: */
-   ret = true;
-   for (int i = 0; i < g_mime_header_list_get_count 
(legacy_display_headers); i++) {
-   GMimeHeader *dh = g_mime_header_list_get_header_at 
(legacy_display_headers, i);
-   if (dh == NULL) {
-   ret = false;
-   goto DONE;
-   }
-   GMimeHeader *ph = g_mime_header_list_get_header (protected_headers, 
g_mime_header_get_name (dh));
-   if (ph == NULL) {
-   ret = false;
-   goto DONE;
-   }
-   const char *dhv = g_mime_header_get_value (dh);
-