[PATCH v5 10/12] cli/new: add --try-decrypt=(true|false)

2017-10-14 Thread Daniel Kahn Gillmor
Try to decrypt any encrypted parts of newly-discovered messages while
indexing them.  The cleartext of any successfully-decrypted messages
will be indexed, with tags applied in the same form as from notmuch
insert --try-decrypt=true.

Note: if the deprecated crypto.gpg_path configuration option is set to
anything other than "gpg", we ignore it (and print a warning on
stderr, if built against gmime < 3.0).

We also add a new test making use of this functionality.  This
requires a bit of reorganization, because we need to allow passing
--long-arguments to "notmuch new" via emacs_fcc_message
---
 completion/notmuch-completion.bash | 13 --
 doc/man1/notmuch-new.rst   | 12 +
 notmuch-new.c  | 10 +++-
 test/T357-index-decryption.sh  | 51 ++
 test/test-lib.sh   | 11 +++-
 5 files changed, 93 insertions(+), 4 deletions(-)
 create mode 100755 test/T357-index-decryption.sh

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index 5201be63..17be6b8f 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -311,11 +311,20 @@ _notmuch_insert()
 _notmuch_new()
 {
 local cur prev words cword split
-_init_completion || return
+_init_completion -s || return
+
+$split &&
+case "${prev}" in
+   --try-decrypt)
+   COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
+   return
+   ;;
+esac
 
+! $split &&
 case "${cur}" in
-*)
-   local options="--no-hooks --quiet ${_notmuch_shared_options}"
+   local options="--no-hooks --try-decrypt= --quiet 
${_notmuch_shared_options}"
compopt -o nospace
COMPREPLY=( $(compgen -W "${options}" -- ${cur}) )
;;
diff --git a/doc/man1/notmuch-new.rst b/doc/man1/notmuch-new.rst
index 6acfa112..c255f980 100644
--- a/doc/man1/notmuch-new.rst
+++ b/doc/man1/notmuch-new.rst
@@ -43,6 +43,18 @@ Supported options for **new** include
 ``--quiet``
 Do not print progress or results.
 
+``--try-decrypt=(true|false)``
+
+If true, when encountering an encrypted message, try to
+decrypt it while indexing.  If decryption is successful, index
+the cleartext itself.  Be aware that the index is likely
+sufficient to reconstruct the cleartext of the message itself,
+so please ensure that the notmuch message index is adequately
+protected.  DO NOT USE ``--try-decrypt=true`` without
+considering the security of your index.
+
+See also ``index.try_decrypt`` in **notmuch-config(1)**.
+
 EXIT STATUS
 ===
 
diff --git a/notmuch-new.c b/notmuch-new.c
index 0f50457e..9b14baf5 100644
--- a/notmuch-new.c
+++ b/notmuch-new.c
@@ -267,7 +267,7 @@ add_file (notmuch_database_t *notmuch, const char *filename,
 if (status)
goto DONE;
 
-status = notmuch_database_index_file (notmuch, filename, NULL, &message);
+status = notmuch_database_index_file (notmuch, filename, 
index_options.opts, &message);
 switch (status) {
 /* Success. */
 case NOTMUCH_STATUS_SUCCESS:
@@ -963,6 +963,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, 
char *argv[])
{ .opt_bool = &verbose, .name = "verbose" },
{ .opt_bool = &add_files_state.debug, .name = "debug" },
{ .opt_bool = &no_hooks, .name = "no-hooks" },
+   { .opt_inherit = notmuch_index_options },
{ .opt_inherit = notmuch_shared_options },
{ }
 };
@@ -1080,6 +1081,13 @@ notmuch_new_command (notmuch_config_t *config, int argc, 
char *argv[])
 if (notmuch == NULL)
return EXIT_FAILURE;
 
+status = notmuch_process_index_options (notmuch, config);
+if (status != NOTMUCH_STATUS_SUCCESS) {
+   fprintf (stderr, "Error: Failed to process index options. (%s)\n",
+notmuch_status_to_string (status));
+   return EXIT_FAILURE;
+}
+
 /* Set up our handler for SIGINT. We do this after having
  * potentially done a database upgrade we this interrupt handler
  * won't support. */
diff --git a/test/T357-index-decryption.sh b/test/T357-index-decryption.sh
new file mode 100755
index ..d7180797
--- /dev/null
+++ b/test/T357-index-decryption.sh
@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+
+# TODO: test index.decryption=failed
+
+test_description='indexing decrypted mail'
+. ./test-lib.sh || exit 1
+
+##
+
+add_gnupg_home
+# get key fingerprint
+FINGERPRINT=$(gpg --no-tty --list-secret-keys --with-colons --fingerprint | 
grep '^fpr:' | cut -d: -f10)
+
+# create a test encrypted message
+test_begin_subtest 'emacs delivery of encrypted message'
+test_expect_success \
+'emacs_fcc_message \
+"test encrypted message for cleartext index 001" \
+"This is a test encrypted message with a wumpus.\n" \
+"(mml-secure-message-encrypt)"'
+
+test

[PATCH v5 06/12] crypto: index encrypted parts when indexopts try_decrypt is set.

2017-10-14 Thread Daniel Kahn Gillmor
If we see index options that ask us to decrypt when indexing a
message, and we encounter an encrypted part, we'll try to descend into
it.

If we can decrypt, we add the property index.decryption=success.

If we can't decrypt (or recognize the encrypted type of mail), we add
the property index.decryption=failure.

Note that a single message may have both values of the
"index-decryption" property: "success" and "failure".  For example,
consider a message that includes multiple layers of encryption.  If we
manage to decrypt the outer layer ("index.decryption=success"), but
fail on the inner layer ("index.decryption=failure").

Because of the property name, this will be automatically cleared (and
possibly re-set) during re-indexing.  This means it will subsequently
correspond to the actual semantics of the stored index.
---
 lib/add-message.cc|  2 +-
 lib/index.cc  | 91 ++-
 lib/message.cc|  4 +--
 lib/notmuch-private.h |  1 +
 4 files changed, 86 insertions(+), 12 deletions(-)

diff --git a/lib/add-message.cc b/lib/add-message.cc
index 34099ed5..f5fac8be 100644
--- a/lib/add-message.cc
+++ b/lib/add-message.cc
@@ -546,7 +546,7 @@ notmuch_database_index_file (notmuch_database_t *notmuch,
indexopts = def_indexopts;
}
 
-   ret = _notmuch_message_index_file (message, message_file);
+   ret = _notmuch_message_index_file (message, indexopts, message_file);
if (ret)
goto DONE;
 
diff --git a/lib/index.cc b/lib/index.cc
index e5ae2ba7..6e684f5f 100644
--- a/lib/index.cc
+++ b/lib/index.cc
@@ -364,9 +364,15 @@ _index_content_type (notmuch_message_t *message, 
GMimeObject *part)
 }
 }
 
+static void
+_index_encrypted_mime_part (notmuch_message_t *message, notmuch_indexopts_t 
*indexopts,
+   GMimeContentType *content_type,
+   GMimeMultipartEncrypted *part);
+
 /* Callback to generate terms for each mime part of a message. */
 static void
 _index_mime_part (notmuch_message_t *message,
+ notmuch_indexopts_t *indexopts,
  GMimeObject *part)
 {
 GMimeStream *stream, *filter;
@@ -385,6 +391,7 @@ _index_mime_part (notmuch_message_t *message,
 }
 
 _index_content_type (message, part);
+content_type = g_mime_object_get_content_type (part);
 
 if (GMIME_IS_MULTIPART (part)) {
GMimeMultipart *multipart = GMIME_MULTIPART (part);
@@ -409,17 +416,21 @@ _index_mime_part (notmuch_message_t *message,
}
}
if (GMIME_IS_MULTIPART_ENCRYPTED (multipart)) {
-   /* Don't index encrypted parts, but index their content type. */
_index_content_type (message,
 g_mime_multipart_get_part (multipart, i));
-   if ((i != GMIME_MULTIPART_ENCRYPTED_VERSION) &&
-   (i != GMIME_MULTIPART_ENCRYPTED_CONTENT)) {
-   _notmuch_database_log (_notmuch_message_database (message),
-  "Warning: Unexpected extra parts of 
multipart/encrypted.\n");
+   if (i == GMIME_MULTIPART_ENCRYPTED_CONTENT) {
+   _index_encrypted_mime_part(message, indexopts,
+  content_type,
+  GMIME_MULTIPART_ENCRYPTED 
(part));
+   } else {
+   if (i != GMIME_MULTIPART_ENCRYPTED_VERSION) {
+   _notmuch_database_log (_notmuch_message_database 
(message),
+  "Warning: Unexpected extra parts 
of multipart/encrypted.\n");
+   }
}
continue;
}
-   _index_mime_part (message,
+   _index_mime_part (message, indexopts,
  g_mime_multipart_get_part (multipart, i));
}
return;
@@ -430,7 +441,7 @@ _index_mime_part (notmuch_message_t *message,
 
mime_message = g_mime_message_part_get_message (GMIME_MESSAGE_PART 
(part));
 
-   _index_mime_part (message, g_mime_message_get_mime_part (mime_message));
+   _index_mime_part (message, indexopts, g_mime_message_get_mime_part 
(mime_message));
 
return;
 }
@@ -464,7 +475,6 @@ _index_mime_part (notmuch_message_t *message,
 
 filter = g_mime_stream_filter_new (stream);
 
-content_type = g_mime_object_get_content_type (part);
 discard_non_term_filter = notmuch_filter_discard_non_term_new 
(content_type);
 
 g_mime_stream_filter_add (GMIME_STREAM_FILTER (filter),
@@ -502,8 +512,71 @@ _index_mime_part (notmuch_message_t *message,
 }
 }
 
+/* descend (if desired) into the cleartext part of an encrypted MIME
+ * part while indexing. */
+static void
+_index_encrypted_mime_part (notmuch_message_t *message,
+   notmuch_indexopts_t *indexopts,
+   g_

[PATCH v5 11/12] cli/insert: add --try-decrypt=(true|false)

2017-10-14 Thread Daniel Kahn Gillmor
Allow an incoming message to be delivered while indexing the
cleartext, on a per-message basis.

This requires the secret keys for the message to be available.  For
the moment, the most functional approach is to ensure that gpg-agent
is running and knows about any secret keys that might be useful to
decrypt incoming mail.

Any additional recommendations for how to phrase the caveat for this
option are welcome.

Note: if the deprecated crypto.gpg_path is set to anything other than
"gpg", we ignore it (and print a warning on stderr, if built against
gmime < 3.0).
---
 completion/notmuch-completion.bash |  6 +-
 doc/man1/notmuch-insert.rst| 14 ++
 notmuch-insert.c   | 14 +++---
 3 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index 17be6b8f..72a75a94 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -287,12 +287,16 @@ _notmuch_insert()
sed "s|^$path/||" | grep -v "\(^\|/\)\(cur\|new\|tmp\)$" ) )
return
;;
+   --try-decrypt)
+   COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
+   return
+   ;;
 esac
 
 ! $split &&
 case "${cur}" in
--*)
-   local options="--create-folder --folder= --keep --no-hooks 
${_notmuch_shared_options}"
+   local options="--create-folder --folder= --keep --no-hooks 
--try-decrypt= ${_notmuch_shared_options}"
compopt -o nospace
COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
return
diff --git a/doc/man1/notmuch-insert.rst b/doc/man1/notmuch-insert.rst
index f79600d6..647dac06 100644
--- a/doc/man1/notmuch-insert.rst
+++ b/doc/man1/notmuch-insert.rst
@@ -50,6 +50,20 @@ Supported options for **insert** include
 ``--no-hooks``
 Prevent hooks from being run.
 
+``--try-decrypt=(true|false)``
+
+If true and the message is encrypted, try to decrypt the
+message while indexing.  If decryption is successful, index
+the cleartext itself.  Either way, the message is always
+stored to disk in its original form (ciphertext).  Be aware
+that the index is likely sufficient to reconstruct the
+cleartext of the message itself, so please ensure that the
+notmuch message index is adequately protected. DO NOT USE
+``--try-decrypt=true`` without considering the security of
+your index.
+
+See also ``index.try_decrypt`` in **notmuch-config(1)**.
+
 EXIT STATUS
 ===
 
diff --git a/notmuch-insert.c b/notmuch-insert.c
index 32be7419..e64655cc 100644
--- a/notmuch-insert.c
+++ b/notmuch-insert.c
@@ -379,12 +379,13 @@ FAIL:
  */
 static notmuch_status_t
 add_file (notmuch_database_t *notmuch, const char *path, tag_op_list_t 
*tag_ops,
- bool synchronize_flags, bool keep)
+ bool synchronize_flags, bool keep,
+ notmuch_indexopts_t *indexopts)
 {
 notmuch_message_t *message;
 notmuch_status_t status;
 
-status = notmuch_database_index_file (notmuch, path, NULL, &message);
+status = notmuch_database_index_file (notmuch, path, indexopts, &message);
 if (status == NOTMUCH_STATUS_SUCCESS) {
status = tag_op_list_apply (message, tag_ops, 0);
if (status) {
@@ -467,6 +468,7 @@ notmuch_insert_command (notmuch_config_t *config, int argc, 
char *argv[])
{ .opt_bool = &create_folder, .name = "create-folder" },
{ .opt_bool = &keep, .name = "keep" },
{ .opt_bool =  &no_hooks, .name = "no-hooks" },
+   { .opt_inherit = notmuch_index_options },
{ .opt_inherit = notmuch_shared_options },
{ }
 };
@@ -545,9 +547,15 @@ notmuch_insert_command (notmuch_config_t *config, int 
argc, char *argv[])
 
 notmuch_exit_if_unmatched_db_uuid (notmuch);
 
+status = notmuch_process_index_options (notmuch, config);
+if (status != NOTMUCH_STATUS_SUCCESS) {
+   fprintf (stderr, "Error: Failed to process index options. (%s)\n",
+notmuch_status_to_string (status));
+   return EXIT_FAILURE;
+}
 
 /* Index the message. */
-status = add_file (notmuch, newpath, tag_ops, synchronize_flags, keep);
+status = add_file (notmuch, newpath, tag_ops, synchronize_flags, keep, 
index_options.opts);
 
 /* Commit changes. */
 close_status = notmuch_database_destroy (notmuch);
-- 
2.14.2

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


Re: [PATCH v3 11/15] config: indexing defaults will be stored in the database.

2017-10-14 Thread Daniel Kahn Gillmor
On Sat 2017-10-14 15:08:51 -0300, David Bremner wrote:
> Daniel Kahn Gillmor  writes:
>
>>  static int
>>  _print_db_config(notmuch_config_t *config, const char *name)
>> @@ -859,6 +860,8 @@ notmuch_config_command_get (notmuch_config_t *config, 
>> char *item)
>>  notmuch_built_with (item + strlen (BUILT_WITH_PREFIX)) ? "true" 
>> : "false");
>>  } else if (STRNCMP_LITERAL (item, QUERY_PREFIX) == 0) {
>>  return _print_db_config (config, item);
>> +} else if (STRNCMP_LITERAL (item, INDEX_PREFIX) == 0) {
>> +return _print_db_config (config, item);
>>  } else {
>>  char **value;
>
> I wonder if we should sanity check the value of 'item' more here. With
> 'query.', it makes sense to get or set anything, since it's just the
> name of a stored query. With 'index.', presumably only certain
> parameters make sense. As a motivating example, consider someone who
> sets
>
> $ notmuch config index.try_decrypt true
>
> then changes their mind
>
> $ notmuch config index.try-decrypt false
>
> They _think_ they are safe, but notmuch is silently going to continue
> decrypting their mail

Do you have any suggestions about how we should do this check?  Some
sort of registry of known options?  If we have such a registry, how
would an addon or a frontend use notmuch config without having to modify
that registry?

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


cleartext indexing version 5

2017-10-14 Thread Daniel Kahn Gillmor
Thanks to everyone for their reviews of this series that provides
indexing of the cleartext of encrypted messages (and thanks to bremner
for already pushing some of the less controversial patches of the
previous revisions of this series).  I hope you'll agree that your
suggestions have improved the propsoal :)

In particular, the following significant changes have been made since
the previous revision:

 * all properties whose key is prefixed with "index." are cleared (and
   possibly re-set) upon reindexing, not just a registered list of
   "autoproperties".

 * handling of command-line options for indexing (i.e. for the "new",
   "reindex", and "insert" subcommands) has been consolidated into a
   centralized location, which not only reduces total code footprint
   but will make it easier to add shared indexing options in the
   future.

I welcome review and feedback.

  --dkg

Series History
--

the first version of this series was sent starting at:

id:20170912230153.4175-1-...@fifthhorseman.net

version 2 of this series was sent as:

id:20170915055359.24123-1-...@fifthhorseman.net

version 3 of this series was sent as:

id:20171010054916.23925-1-...@fifthhorseman.net

and version 4 was only partially sent to the list.

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


[PATCH v5 05/12] reindex: drop all properties named with prefix "index."

2017-10-14 Thread Daniel Kahn Gillmor
This allows us to create new properties that will be automatically set
during indexing, and cleared during re-indexing, just by choice of
property name.
---
 lib/message.cc | 6 ++
 lib/notmuch.h  | 6 ++
 2 files changed, 12 insertions(+)

diff --git a/lib/message.cc b/lib/message.cc
index 4ab0ed26..77bb6c76 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -1999,6 +1999,12 @@ notmuch_message_reindex (notmuch_message_t *message,
goto DONE;
 }
 
+ret = notmuch_message_remove_all_properties_with_prefix (message, 
"index.");
+if (ret) {
+   INTERNAL_ERROR ("failed to remove index.* properties");
+   goto DONE;
+}
+
 /* re-add the filenames with the associated indexopts */
 for (; notmuch_filenames_valid (orig_filenames);
 notmuch_filenames_move_to_next (orig_filenames)) {
diff --git a/lib/notmuch.h b/lib/notmuch.h
index 817f357f..fd6ead81 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -1765,6 +1765,12 @@ notmuch_message_destroy (notmuch_message_t *message);
  * add or delete values for, as other subsystems or extensions may
  * depend on these properties.
  *
+ * Notmuch has some conventions about how certain properties are
+ * treated.  Those conventions include:
+ *
+ * - preperties whose name begins with "index." are cleared (and
+ *   possibly re-set) upon reindexing.
+ *
  */
 /**@{*/
 /**
-- 
2.14.2

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


[PATCH v5 01/12] crypto: move into libnotmuch_util

2017-10-14 Thread Daniel Kahn Gillmor
This prepares us for using the crypto object in both libnotmuch and
the client.
---
 Makefile.local|  1 -
 notmuch-client.h  | 22 +-
 util/Makefile.local   |  2 +-
 crypto.c => util/crypto.c |  7 ++-
 util/crypto.h | 28 
 5 files changed, 36 insertions(+), 24 deletions(-)
 rename crypto.c => util/crypto.c (96%)
 create mode 100644 util/crypto.h

diff --git a/Makefile.local b/Makefile.local
index 9d9c52c2..9505b7fe 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -246,7 +246,6 @@ notmuch_client_srcs =   \
sprinter-text.c \
query-string.c  \
mime-node.c \
-   crypto.c\
tag-util.c
 
 notmuch_client_modules = $(notmuch_client_srcs:.c=.o)
diff --git a/notmuch-client.h b/notmuch-client.h
index bdcfd893..d17cdf01 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -32,9 +32,6 @@
 
 #include "gmime-extra.h"
 
-/* This is automatically included only since gmime 2.6.10 */
-#include 
-
 #include "notmuch.h"
 
 /* This is separate from notmuch-private.h because we're trying to
@@ -54,6 +51,7 @@
 #include 
 
 #include "talloc-extra.h"
+#include "crypto.h"
 
 #define unused(x) x __attribute__ ((unused))
 
@@ -71,16 +69,6 @@ typedef struct notmuch_show_format {
  const struct notmuch_show_params *params);
 } notmuch_show_format_t;
 
-typedef struct _notmuch_crypto {
-bool verify;
-bool decrypt;
-#if (GMIME_MAJOR_VERSION < 3)
-GMimeCryptoContext* gpgctx;
-GMimeCryptoContext* pkcs7ctx;
-const char *gpgpath;
-#endif
-} _notmuch_crypto_t;
-
 typedef struct notmuch_show_params {
 bool entire_thread;
 bool omit_excluded;
@@ -180,14 +168,6 @@ typedef struct _notmuch_config notmuch_config_t;
 void
 notmuch_exit_if_unsupported_format (void);
 
-#if (GMIME_MAJOR_VERSION <3)
-GMimeCryptoContext *
-_notmuch_crypto_get_gmime_context (_notmuch_crypto_t *crypto, const char 
*protocol);
-#endif
-
-void
-_notmuch_crypto_cleanup (_notmuch_crypto_t *crypto);
-
 int
 notmuch_count_command (notmuch_config_t *config, int argc, char *argv[]);
 
diff --git a/util/Makefile.local b/util/Makefile.local
index 3027880b..ba03230e 100644
--- a/util/Makefile.local
+++ b/util/Makefile.local
@@ -5,7 +5,7 @@ extra_cflags += -I$(srcdir)/$(dir)
 
 libnotmuch_util_c_srcs := $(dir)/xutil.c $(dir)/error_util.c 
$(dir)/hex-escape.c \
  $(dir)/string-util.c $(dir)/talloc-extra.c 
$(dir)/zlib-extra.c \
-   $(dir)/util.c $(dir)/gmime-extra.c
+   $(dir)/util.c $(dir)/gmime-extra.c $(dir)/crypto.c
 
 libnotmuch_util_modules := $(libnotmuch_util_c_srcs:.c=.o)
 
diff --git a/crypto.c b/util/crypto.c
similarity index 96%
rename from crypto.c
rename to util/crypto.c
index 4c1b7eec..c51d67ed 100644
--- a/crypto.c
+++ b/util/crypto.c
@@ -18,7 +18,12 @@
  * Authors: Jameson Rollins 
  */
 
-#include "notmuch-client.h"
+#include "crypto.h"
+#include 
+#define unused(x) x __attribute__ ((unused))
+
+#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0]))
+
 #if (GMIME_MAJOR_VERSION < 3)
 /* Create a GPG context (GMime 2.6) */
 static GMimeCryptoContext *
diff --git a/util/crypto.h b/util/crypto.h
new file mode 100644
index ..80628dc5
--- /dev/null
+++ b/util/crypto.h
@@ -0,0 +1,28 @@
+#ifndef _CRYPTO_H
+#define _CRYPTO_H
+
+#include 
+#if (GMIME_MAJOR_VERSION < 3)
+#include "gmime-extra.h"
+#endif
+
+typedef struct _notmuch_crypto {
+bool verify;
+bool decrypt;
+#if (GMIME_MAJOR_VERSION < 3)
+GMimeCryptoContext* gpgctx;
+GMimeCryptoContext* pkcs7ctx;
+const char *gpgpath;
+#endif
+} _notmuch_crypto_t;
+
+
+#if (GMIME_MAJOR_VERSION < 3)
+GMimeCryptoContext *
+_notmuch_crypto_get_gmime_context (_notmuch_crypto_t *crypto, const char 
*protocol);
+#endif
+
+void
+_notmuch_crypto_cleanup (_notmuch_crypto_t *crypto);
+
+#endif
-- 
2.14.2

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


[PATCH v5 04/12] properties: add notmuch_message_remove_all_properties_with_prefix()

2017-10-14 Thread Daniel Kahn Gillmor
Subsequent patches will introduce a convention that properties whose
name starts with "index." will be stripped (and possibly re-added)
during re-indexing.  This patch lays the groundwork for doing that.
---
 lib/message-property.cc | 18 --
 lib/notmuch.h   | 16 
 2 files changed, 32 insertions(+), 2 deletions(-)

diff --git a/lib/message-property.cc b/lib/message-property.cc
index d72c74c3..35eaf3c6 100644
--- a/lib/message-property.cc
+++ b/lib/message-property.cc
@@ -85,8 +85,9 @@ notmuch_message_remove_property (notmuch_message_t *message, 
const char *key, co
 return _notmuch_message_modify_property (message, key, value, true);
 }
 
+static
 notmuch_status_t
-notmuch_message_remove_all_properties (notmuch_message_t *message, const char 
*key)
+_notmuch_message_remove_all_properties (notmuch_message_t *message, const char 
*key, bool prefix)
 {
 notmuch_status_t status;
 const char * term_prefix;
@@ -97,7 +98,8 @@ notmuch_message_remove_all_properties (notmuch_message_t 
*message, const char *k
 
 _notmuch_message_invalidate_metadata (message, "property");
 if (key)
-   term_prefix = talloc_asprintf (message, "%s%s=", _find_prefix 
("property"), key);
+   term_prefix = talloc_asprintf (message, "%s%s%s", _find_prefix 
("property"), key,
+  prefix ? "" : "=");
 else
term_prefix = _find_prefix ("property");
 
@@ -107,6 +109,18 @@ notmuch_message_remove_all_properties (notmuch_message_t 
*message, const char *k
 return NOTMUCH_STATUS_SUCCESS;
 }
 
+notmuch_status_t
+notmuch_message_remove_all_properties (notmuch_message_t *message, const char 
*key)
+{
+return _notmuch_message_remove_all_properties (message, key, false);
+}
+
+notmuch_status_t
+notmuch_message_remove_all_properties_with_prefix (notmuch_message_t *message, 
const char *prefix)
+{
+return _notmuch_message_remove_all_properties (message, prefix, true);
+}
+
 notmuch_message_properties_t *
 notmuch_message_get_properties (notmuch_message_t *message, const char *key, 
notmuch_bool_t exact)
 {
diff --git a/lib/notmuch.h b/lib/notmuch.h
index 0b2e8305..817f357f 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -1824,6 +1824,22 @@ notmuch_message_remove_property (notmuch_message_t 
*message, const char *key, co
 notmuch_status_t
 notmuch_message_remove_all_properties (notmuch_message_t *message, const char 
*key);
 
+/**
+ * Remove all (prefix*,value) pairs from the given message
+ *
+ * @param[in,out] message  message to operate on.
+ * @param[in] prefix   delete properties with keys that start with prefix.
+ *If NULL, delete all properties
+ * @returns
+ * - NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in
+ *   read-only mode so message cannot be modified.
+ * - NOTMUCH_STATUS_SUCCESS: No error occured.
+ *
+ * @since libnotmuch 5.1 (notmuch 0.26)
+ */
+notmuch_status_t
+notmuch_message_remove_all_properties_with_prefix (notmuch_message_t *message, 
const char *prefix);
+
 /**
  * Opaque message property iterator
  */
-- 
2.14.2

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


[PATCH v5 09/12] cli: set up shared command-line arguments for indexing

2017-10-14 Thread Daniel Kahn Gillmor
We have an indexopts structure for manipulating indexing in different
ways, but we also have three command-line invocations that can trigger
indexing: new, insert, and reindex.

This changeset prepares a common parser that these subcommands can
share.

At the moment, it's just --try-decrypt, but others will likely follow.
---
 notmuch-client.h | 12 
 notmuch.c| 39 +++
 2 files changed, 51 insertions(+)

diff --git a/notmuch-client.h b/notmuch-client.h
index d17cdf01..27852360 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -495,4 +495,16 @@ void notmuch_exit_if_unmatched_db_uuid (notmuch_database_t 
*notmuch);
 void notmuch_process_shared_options (const char* subcommand_name);
 int notmuch_minimal_options (const char* subcommand_name,
 int argc, char **argv);
+
+
+struct _notmuch_client_index_options {
+bool try_decrypt;
+bool try_decrypt_set;
+notmuch_indexopts_t * opts;
+};
+extern struct _notmuch_client_index_options index_options;
+extern const notmuch_opt_desc_t  notmuch_index_options [];
+notmuch_status_t
+notmuch_process_index_options (notmuch_database_t *notmuch, notmuch_config_t 
*config);
+
 #endif
diff --git a/notmuch.c b/notmuch.c
index 02148f2e..f83b5a6f 100644
--- a/notmuch.c
+++ b/notmuch.c
@@ -96,6 +96,45 @@ int notmuch_minimal_options (const char *subcommand_name,
 return opt_index;
 }
 
+
+struct _notmuch_client_index_options index_options = { };
+const notmuch_opt_desc_t  notmuch_index_options [] = {
+{ .opt_bool = &index_options.try_decrypt,
+  .present = &index_options.try_decrypt_set,
+  .name = "try-decrypt" },
+{ }
+};
+
+
+notmuch_status_t
+notmuch_process_index_options (notmuch_database_t *notmuch, 
g_mime_3_unused(notmuch_config_t *config))
+{
+if (index_options.opts == NULL)
+   index_options.opts = notmuch_database_get_default_indexopts (notmuch);
+if (index_options.try_decrypt_set) {
+   notmuch_status_t status;
+   if (index_options.opts == NULL)
+   return NOTMUCH_STATUS_OUT_OF_MEMORY;
+   status = notmuch_indexopts_set_try_decrypt (index_options.opts, 
index_options.try_decrypt);
+   if (status != NOTMUCH_STATUS_SUCCESS) {
+   fprintf (stderr, "Error: Failed to set try_decrypt to %s. (%s)\n",
+index_options.try_decrypt ? "True" : "False", 
notmuch_status_to_string (status));
+   notmuch_indexopts_destroy (index_options.opts);
+   return status;
+   }
+}
+#if (GMIME_MAJOR_VERSION < 3)
+if (index_options.opts && notmuch_indexopts_get_try_decrypt 
(index_options.opts)) {
+   const char* gpg_path = notmuch_config_get_crypto_gpg_path (config);
+   if (gpg_path && strcmp(gpg_path, "gpg"))
+   fprintf (stderr, "Warning: deprecated crypto.gpg_path is set to 
'%s'\n"
+"\tbut ignoring (use $PATH instead)\n", gpg_path);
+}
+#endif
+return NOTMUCH_STATUS_SUCCESS;
+}
+
+
 static command_t commands[] = {
 { NULL, notmuch_command, NOTMUCH_CONFIG_OPEN | NOTMUCH_CONFIG_CREATE,
   "Notmuch main command." },
-- 
2.14.2

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


[PATCH v5 02/12] crypto: make shared crypto code behave library-like

2017-10-14 Thread Daniel Kahn Gillmor
If we're going to reuse the crypto code across both the library and
the client, then it needs to report error states properly and not
write to stderr.
---
 lib/database.cc |  6 
 lib/notmuch.h   | 17 +++
 mime-node.c |  7 -
 util/crypto.c   | 92 -
 util/crypto.h   |  7 +++--
 5 files changed, 79 insertions(+), 50 deletions(-)

diff --git a/lib/database.cc b/lib/database.cc
index 35c66939..02444e09 100644
--- a/lib/database.cc
+++ b/lib/database.cc
@@ -413,6 +413,12 @@ notmuch_status_to_string (notmuch_status_t status)
return "Operation requires a database upgrade";
 case NOTMUCH_STATUS_PATH_ERROR:
return "Path supplied is illegal for this function";
+case NOTMUCH_STATUS_MALFORMED_CRYPTO_PROTOCOL:
+   return "Crypto protocol missing, malformed, or unintelligible";
+case NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION:
+   return "Crypto engine initialization failure";
+case NOTMUCH_STATUS_UNKNOWN_CRYPTO_PROTOCOL:
+   return "Unknown crypto protocol";
 default:
 case NOTMUCH_STATUS_LAST_STATUS:
return "Unknown error status value";
diff --git a/lib/notmuch.h b/lib/notmuch.h
index e8e0cc12..669e01b1 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -191,6 +191,23 @@ typedef enum _notmuch_status {
  * function, in a way not covered by a more specific argument.
  */
 NOTMUCH_STATUS_ILLEGAL_ARGUMENT,
+/**
+ * A MIME object claimed to have cryptographic protection which
+ * notmuch tried to handle, but the protocol was not specified in
+ * an intelligible way.
+ */
+NOTMUCH_STATUS_MALFORMED_CRYPTO_PROTOCOL,
+/**
+ * Notmuch attempted to do crypto processing, but could not
+ * initialize the engine needed to do so.
+ */
+NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION,
+/**
+ * A MIME object claimed to have cryptographic protection, and
+ * notmuch attempted to process it, but the specific protocol was
+ * something that notmuch doesn't know how to handle.
+ */
+NOTMUCH_STATUS_UNKNOWN_CRYPTO_PROTOCOL,
 /**
  * Not an actual status value. Just a way to find out how many
  * valid status values there are.
diff --git a/mime-node.c b/mime-node.c
index d48be4c4..c3d5cb9b 100644
--- a/mime-node.c
+++ b/mime-node.c
@@ -269,7 +269,12 @@ _mime_node_create (mime_node_t *parent, GMimeObject *part)
|| (GMIME_IS_MULTIPART_SIGNED (part) && node->ctx->crypto->verify)) {
GMimeContentType *content_type = g_mime_object_get_content_type (part);
const char *protocol = g_mime_content_type_get_parameter (content_type, 
"protocol");
-   cryptoctx = _notmuch_crypto_get_gmime_context (node->ctx->crypto, 
protocol);
+   notmuch_status_t status;
+   status = _notmuch_crypto_get_gmime_ctx_for_protocol (node->ctx->crypto,
+protocol, 
&cryptoctx);
+   if (status) /* this is a warning, not an error */
+   fprintf (stderr, "Warning: %s (%s).\n", notmuch_status_to_string 
(status),
+protocol ? protocol : "NULL");
if (!cryptoctx)
return NULL;
 }
diff --git a/util/crypto.c b/util/crypto.c
index c51d67ed..5c84282e 100644
--- a/util/crypto.c
+++ b/util/crypto.c
@@ -25,85 +25,86 @@
 #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0]))
 
 #if (GMIME_MAJOR_VERSION < 3)
-/* Create a GPG context (GMime 2.6) */
-static GMimeCryptoContext *
-create_gpg_context (_notmuch_crypto_t *crypto)
+/* Create or pass on a GPG context (GMime 2.6) */
+static notmuch_status_t
+get_gpg_context (_notmuch_crypto_t *crypto, GMimeCryptoContext **ctx)
 {
-GMimeCryptoContext *gpgctx;
+if (ctx == NULL || crypto == NULL)
+   return NOTMUCH_STATUS_NULL_POINTER;
 
-if (crypto->gpgctx)
-   return crypto->gpgctx;
+if (crypto->gpgctx) {
+   *ctx = crypto->gpgctx;
+   return NOTMUCH_STATUS_SUCCESS;
+}
 
 /* TODO: GMimePasswordRequestFunc */
-gpgctx = g_mime_gpg_context_new (NULL, crypto->gpgpath ? crypto->gpgpath : 
"gpg");
-if (! gpgctx) {
-   fprintf (stderr, "Failed to construct gpg context.\n");
-   return NULL;
+crypto->gpgctx = g_mime_gpg_context_new (NULL, crypto->gpgpath ? 
crypto->gpgpath : "gpg");
+if (! crypto->gpgctx) {
+   return NOTMUCH_STATUS_FAILED_CRYPTO_CONTEXT_CREATION;
 }
-crypto->gpgctx = gpgctx;
 
-g_mime_gpg_context_set_use_agent ((GMimeGpgContext *) gpgctx, true);
-g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) gpgctx, false);
+g_mime_gpg_context_set_use_agent ((GMimeGpgContext *) crypto->gpgctx, 
true);
+g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) crypto->gpgctx, 
false);
 
-return gpgctx;
+*ctx = crypto->gpgctx;
+return NOTMUCH_STATUS_SUCCESS;
 }
 
-/* Create a PKCS7 context (GMime 2.6) */
-static GMimeCryptoContext *
-create_pkcs7_context (_notmuch_crypto_t *cryp

[PATCH v5 12/12] cli/reindex: add --try-decrypt=(true|false)

2017-10-14 Thread Daniel Kahn Gillmor
Try to decrypt any encrypted parts of newly-discovered messages while
re-indexing them.  The cleartext of any successfully-decrypted
messages will be indexed, with tags applied in the same form as from
notmuch insert --try-decrypt=true.

Note: if the deprecated crypto.gpg_path configuration option is set to
anything other than "gpg", we ignore it (and print a warning on
stderr, if built against gmime < 3.0).
---
 completion/notmuch-completion.bash | 10 +-
 doc/man1/notmuch-reindex.rst   | 14 
 notmuch-reindex.c  | 12 +--
 test/T357-index-decryption.sh  | 65 ++
 4 files changed, 98 insertions(+), 3 deletions(-)

diff --git a/completion/notmuch-completion.bash 
b/completion/notmuch-completion.bash
index 72a75a94..7aae4297 100644
--- a/completion/notmuch-completion.bash
+++ b/completion/notmuch-completion.bash
@@ -435,10 +435,18 @@ _notmuch_reindex()
 local cur prev words cword split
 _init_completion -s || return
 
+$split &&
+case "${prev}" in
+   --try-decrypt)
+   COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
+   return
+   ;;
+esac
+
 ! $split &&
 case "${cur}" in
-*)
-   local options="${_notmuch_shared_options}"
+   local options="--try-decrypt= ${_notmuch_shared_options}"
compopt -o nospace
COMPREPLY=( $(compgen -W "$options" -- ${cur}) )
;;
diff --git a/doc/man1/notmuch-reindex.rst b/doc/man1/notmuch-reindex.rst
index e39cc4ee..60a060a7 100644
--- a/doc/man1/notmuch-reindex.rst
+++ b/doc/man1/notmuch-reindex.rst
@@ -19,6 +19,20 @@ The **reindex** command searches for all messages matching 
the
 supplied search terms, and re-creates the full-text index on these
 messages using the supplied options.
 
+Supported options for **reindex** include
+
+``--try-decrypt=(true|false)``
+
+If true, when encountering an encrypted message, try to
+decrypt it while reindexing.  If decryption is successful,
+index the cleartext itself.  Be aware that the index is likely
+sufficient to reconstruct the cleartext of the message itself,
+so please ensure that the notmuch message index is adequately
+protected. DO NOT USE ``--try-decrypt=true`` without
+considering the security of your index.
+
+See also ``index.try_decrypt`` in **notmuch-config(1)**.
+
 SEE ALSO
 
 
diff --git a/notmuch-reindex.c b/notmuch-reindex.c
index 57ff5904..2b06ec6b 100644
--- a/notmuch-reindex.c
+++ b/notmuch-reindex.c
@@ -89,7 +89,7 @@ notmuch_reindex_command (notmuch_config_t *config, int argc, 
char *argv[])
 struct sigaction action;
 int opt_index;
 int ret;
-notmuch_indexopts_t *indexopts = NULL;
+notmuch_status_t status;
 
 /* Set up our handler for SIGINT */
 memset (&action, 0, sizeof (struct sigaction));
@@ -99,6 +99,7 @@ notmuch_reindex_command (notmuch_config_t *config, int argc, 
char *argv[])
 sigaction (SIGINT, &action, NULL);
 
 notmuch_opt_desc_t options[] = {
+   { .opt_inherit = notmuch_index_options },
{ .opt_inherit = notmuch_shared_options },
{ }
 };
@@ -115,6 +116,13 @@ notmuch_reindex_command (notmuch_config_t *config, int 
argc, char *argv[])
 
 notmuch_exit_if_unmatched_db_uuid (notmuch);
 
+status = notmuch_process_index_options (notmuch, config);
+if (status != NOTMUCH_STATUS_SUCCESS) {
+   fprintf (stderr, "Error: Failed to process index options. (%s)\n",
+notmuch_status_to_string (status));
+   return EXIT_FAILURE;
+}
+
 query_string = query_string_from_args (config, argc-opt_index, 
argv+opt_index);
 if (query_string == NULL) {
fprintf (stderr, "Out of memory\n");
@@ -126,7 +134,7 @@ notmuch_reindex_command (notmuch_config_t *config, int 
argc, char *argv[])
return EXIT_FAILURE;
 }
 
-ret = reindex_query (notmuch, query_string, indexopts);
+ret = reindex_query (notmuch, query_string, index_options.opts);
 
 notmuch_database_destroy (notmuch);
 
diff --git a/test/T357-index-decryption.sh b/test/T357-index-decryption.sh
index d7180797..cd97c48f 100755
--- a/test/T357-index-decryption.sh
+++ b/test/T357-index-decryption.sh
@@ -48,4 +48,69 @@ test_expect_equal \
 "$output" \
 "$expected"
 
+# add a tag to all messages to ensure that it stays after reindexing
+test_begin_subtest 'tagging all messages'
+test_expect_success 'notmuch tag +blarney "encrypted message"'
+test_begin_subtest "verify that tags have not changed"
+output=$(notmuch search tag:blarney)
+expected='thread:0001   2000-01-01 [1/1] Notmuch Test Suite; test 
encrypted message for cleartext index 001 (blarney encrypted inbox)
+thread:0002   2000-01-01 [1/1] Notmuch Test Suite; test encrypted 
message for cleartext index 002 (blarney encrypted inbox)'
+test_expect_equal \
+"$output" \
+"$expected"
+
+# se

[PATCH v5 07/12] config: indexing defaults will be stored in the database.

2017-10-14 Thread Daniel Kahn Gillmor
At indexing time, the database needs to know its internal defaults.
It shouldn't be contingent on an external config file (since that
can't be retrieved from the database object itself).

This behaves the same as the query.* configurations, which are also
stored in the database itself, so we're not introducing any new
dependencies.
---
 notmuch-config.c | 6 ++
 1 file changed, 6 insertions(+)

diff --git a/notmuch-config.c b/notmuch-config.c
index 8fb59f96..e82f7dd7 100644
--- a/notmuch-config.c
+++ b/notmuch-config.c
@@ -809,6 +809,7 @@ _item_split (char *item, char **group, char **key)
 
 #define BUILT_WITH_PREFIX "built_with."
 #define QUERY_PREFIX "query."
+#define INDEX_PREFIX "index."
 
 static int
 _print_db_config(notmuch_config_t *config, const char *name)
@@ -859,6 +860,8 @@ notmuch_config_command_get (notmuch_config_t *config, char 
*item)
notmuch_built_with (item + strlen (BUILT_WITH_PREFIX)) ? "true" 
: "false");
 } else if (STRNCMP_LITERAL (item, QUERY_PREFIX) == 0) {
return _print_db_config (config, item);
+} else if (STRNCMP_LITERAL (item, INDEX_PREFIX) == 0) {
+   return _print_db_config (config, item);
 } else {
char **value;
size_t i, length;
@@ -931,6 +934,9 @@ notmuch_config_command_set (notmuch_config_t *config, char 
*item, int argc, char
 if (STRNCMP_LITERAL (item, QUERY_PREFIX) == 0) {
return _set_db_config (config, item, argc, argv);
 }
+if (STRNCMP_LITERAL (item, INDEX_PREFIX) == 0) {
+   return _set_db_config (config, item, argc, argv);
+}
 
 if (_item_split (item, &group, &key))
return 1;
-- 
2.14.2

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


[PATCH v5 08/12] config: define new option index.try_decrypt

2017-10-14 Thread Daniel Kahn Gillmor
By default, notmuch won't try to decrypt on indexing.  With this
patch, we make it possible to indicate a per-database preference using
the config variable "index.try_decrypt", which by default will be
false.
---
 doc/man1/notmuch-config.rst | 12 
 lib/indexopts.c | 18 +-
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst
index 6a51e64f..6f35d127 100644
--- a/doc/man1/notmuch-config.rst
+++ b/doc/man1/notmuch-config.rst
@@ -134,6 +134,18 @@ The available configuration items are described below.
 
 Default: ``gpg``.
 
+**index.try_decrypt**
+
+When indexing an encrypted e-mail message, if this variable is
+set to true, notmuch will try to decrypt the message and index
+the cleartext.  Be aware that the index is likely sufficient
+to reconstruct the cleartext of the message itself, so please
+ensure that the notmuch message index is adequately protected.
+DO NOT USE ``index.try_decrypt=true`` without considering the
+security of your index.
+
+Default: ``false``.
+
 **built_with.**
 
 Compile time feature . Current possibilities include
diff --git a/lib/indexopts.c b/lib/indexopts.c
index cc1d6422..987d8952 100644
--- a/lib/indexopts.c
+++ b/lib/indexopts.c
@@ -23,7 +23,23 @@
 notmuch_indexopts_t *
 notmuch_database_get_default_indexopts (notmuch_database_t *db)
 {
-return talloc_zero (db, notmuch_indexopts_t);
+notmuch_indexopts_t *ret = talloc_zero (db, notmuch_indexopts_t);
+if (!ret)
+   return ret;
+
+char * try_decrypt;
+notmuch_status_t err = notmuch_database_get_config (db, 
"index.try_decrypt", &try_decrypt);
+if (err)
+   return ret;
+
+if (try_decrypt &&
+   ((!(strcasecmp(try_decrypt, "true"))) ||
+(!(strcasecmp(try_decrypt, "yes"))) ||
+(!(strcasecmp(try_decrypt, "1")
+   notmuch_indexopts_set_try_decrypt (ret, true);
+
+free (try_decrypt);
+return ret;
 }
 
 notmuch_status_t
-- 
2.14.2

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


[PATCH v5 03/12] index: implement notmuch_indexopts_t with try_decrypt

2017-10-14 Thread Daniel Kahn Gillmor
This is currently mostly a wrapper around _notmuch_crypto_t that keeps
its internals private and doesn't expose any of the GMime API.
However, non-crypto indexing options might also be added later
(e.g. filters or other transformations).
---
 lib/add-message.cc| 11 ++-
 lib/indexopts.c   | 22 --
 lib/notmuch-private.h |  7 +++
 lib/notmuch.h | 20 
 4 files changed, 57 insertions(+), 3 deletions(-)

diff --git a/lib/add-message.cc b/lib/add-message.cc
index bce10a0f..34099ed5 100644
--- a/lib/add-message.cc
+++ b/lib/add-message.cc
@@ -460,7 +460,7 @@ _notmuch_database_link_message (notmuch_database_t *notmuch,
 notmuch_status_t
 notmuch_database_index_file (notmuch_database_t *notmuch,
 const char *filename,
-notmuch_indexopts_t unused (*indexopts),
+notmuch_indexopts_t *indexopts,
 notmuch_message_t **message_ret)
 {
 notmuch_message_file_t *message_file;
@@ -468,6 +468,7 @@ notmuch_database_index_file (notmuch_database_t *notmuch,
 notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS, ret2;
 notmuch_private_status_t private_status;
 bool is_ghost = false, is_new = false;
+notmuch_indexopts_t *def_indexopts = NULL;
 
 const char *date;
 const char *from, *to, *subject;
@@ -540,6 +541,11 @@ notmuch_database_index_file (notmuch_database_t *notmuch,
if (is_new || is_ghost)
_notmuch_message_set_header_values (message, date, from, subject);
 
+   if (!indexopts) {
+   def_indexopts = notmuch_database_get_default_indexopts (notmuch);
+   indexopts = def_indexopts;
+   }
+
ret = _notmuch_message_index_file (message, message_file);
if (ret)
goto DONE;
@@ -557,6 +563,9 @@ notmuch_database_index_file (notmuch_database_t *notmuch,
 }
 
   DONE:
+if (def_indexopts)
+   notmuch_indexopts_destroy (def_indexopts);
+
 if (message) {
if ((ret == NOTMUCH_STATUS_SUCCESS ||
 ret == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) && message_ret)
diff --git a/lib/indexopts.c b/lib/indexopts.c
index 2f9b841b..cc1d6422 100644
--- a/lib/indexopts.c
+++ b/lib/indexopts.c
@@ -21,9 +21,27 @@
 #include "notmuch-private.h"
 
 notmuch_indexopts_t *
-notmuch_database_get_default_indexopts (notmuch_database_t unused (*db))
+notmuch_database_get_default_indexopts (notmuch_database_t *db)
 {
-return NULL;
+return talloc_zero (db, notmuch_indexopts_t);
+}
+
+notmuch_status_t
+notmuch_indexopts_set_try_decrypt (notmuch_indexopts_t *indexopts,
+  bool try_decrypt)
+{
+if (!indexopts)
+   return NOTMUCH_STATUS_NULL_POINTER;
+indexopts->crypto.decrypt = try_decrypt;
+return NOTMUCH_STATUS_SUCCESS;
+}
+
+bool
+notmuch_indexopts_get_try_decrypt (const notmuch_indexopts_t *indexopts)
+{
+if (!indexopts)
+   return false;
+return indexopts->crypto.decrypt;
 }
 
 void
diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h
index e86f4582..4c408396 100644
--- a/lib/notmuch-private.h
+++ b/lib/notmuch-private.h
@@ -52,6 +52,7 @@ NOTMUCH_BEGIN_DECLS
 #include "xutil.h"
 #include "error_util.h"
 #include "string-util.h"
+#include "crypto.h"
 
 #ifdef DEBUG
 # define DEBUG_DATABASE_SANITY 1
@@ -633,6 +634,12 @@ _notmuch_thread_create (void *ctx,
notmuch_exclude_t omit_exclude,
notmuch_sort_t sort);
 
+/* indexopts.c */
+
+struct _notmuch_indexopts {
+_notmuch_crypto_t crypto;
+};
+
 NOTMUCH_END_DECLS
 
 #ifdef __cplusplus
diff --git a/lib/notmuch.h b/lib/notmuch.h
index 669e01b1..0b2e8305 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -42,6 +42,7 @@
 NOTMUCH_BEGIN_DECLS
 
 #include 
+#include 
 
 #pragma GCC visibility push(default)
 
@@ -2214,6 +2215,25 @@ notmuch_config_list_destroy (notmuch_config_list_t 
*config_list);
 notmuch_indexopts_t *
 notmuch_database_get_default_indexopts (notmuch_database_t *db);
 
+/**
+ * Specify whether to decrypt encrypted parts while indexing.
+ *
+ * Be aware that the index is likely sufficient to reconstruct the
+ * cleartext of the message itself, so please ensure that the notmuch
+ * message index is adequately protected. DO NOT SET THIS FLAG TO TRUE
+ * without considering the security of your index.
+ */
+notmuch_status_t
+notmuch_indexopts_set_try_decrypt (notmuch_indexopts_t *indexopts,
+  bool try_decrypt);
+
+/**
+ * Return whether to decrypt encrypted parts while indexing.
+ * see notmuch_indexopts_set_try_decrypt.
+ */
+bool
+notmuch_indexopts_get_try_decrypt (const notmuch_indexopts_t *indexopts);
+
 /**
  * Destroy a notmuch_indexopts_t object.
  *
-- 
2.14.2

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


Re: [PATCH 1/3] cli: add support for --no- prefixed boolean and keyword flag arguments

2017-10-14 Thread William Casarin

Tested ACK 1-3 + id:20171014201836.4486-1-j...@nikula.org
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH 1/3] cli: add support for --no- prefixed boolean and keyword flag arguments

2017-10-14 Thread William Casarin
Jani Nikula  writes:

> *blush* I screwed those enums up. Here's a patch that takes care of both
> issues id:20171014201836.4486-1-j...@nikula.org. It's independent of
> this series.

Works, thanks.

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


Re: [PATCH 1/3] cli: add support for --no- prefixed boolean and keyword flag arguments

2017-10-14 Thread Jani Nikula
On Sat, 14 Oct 2017, William Casarin  wrote:
> Hey Jani,
>
> Patches look good so far, concept ack for sure.
>
>
> Jani Nikula  writes:
>
>> For example, you can use --no-exclude instead of --exclude=false in
>> notmuch show. If we had keyword flag arguments with some flags
>> defaulting to on, say --include=tags in notmuch dump/restore, this
>> would allow --no-include=tags to switch that off while not affecting
>> other flags.
>
> I've been testing it a bit, I can't seem to make this work in this example:
>
> ./notmuch count --no-exclude
>
> After some brief investigation it might be because count is using
> EXCLUDE_true(1) and EXCLUDE_false(0) which are not equal to
> NOTMUCH_EXCLUDE_TRUE(1) and NOTMUCH_EXCLUDE_FALSE(2), but I'm not sure.

*blush* I screwed those enums up. Here's a patch that takes care of both
issues id:20171014201836.4486-1-j...@nikula.org. It's independent of
this series.

BR,
Jani.


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


[PATCH] cli: make notmuch count --exclude a boolean argument

2017-10-14 Thread Jani Nikula
Commit 0f314c0c99be ("cli: convert notmuch_bool_t to stdbool")
over-eagerly converted EXCLUDE_TRUE and EXCLUDE_FALSE to EXCLUDE_true
and EXCLUDE_false in notmuch-count.c. We could just fix the case back,
but convert the option to an actual boolean argument instead.

We've used a keyword argument rather than a boolean argument for
notmuch count --exclude for five years, since commit 785c1e497f05
("cli: move count to the new --exclude=(true|false|flag) naming
scheme."), "to allow future options to be added more easily". I think
we can conclude future options aren't coming any time soon.
---
 notmuch-count.c | 15 +++
 1 file changed, 3 insertions(+), 12 deletions(-)

diff --git a/notmuch-count.c b/notmuch-count.c
index 1ae7d5146d92..ca05c9793b70 100644
--- a/notmuch-count.c
+++ b/notmuch-count.c
@@ -27,12 +27,6 @@ enum {
 OUTPUT_FILES,
 };
 
-/* The following is to allow future options to be added more easily */
-enum {
-EXCLUDE_true,
-EXCLUDE_false,
-};
-
 /* Return the number of files matching the query, or -1 for an error */
 static int
 count_files (notmuch_query_t *query)
@@ -160,7 +154,7 @@ notmuch_count_command (notmuch_config_t *config, int argc, 
char *argv[])
 char *query_str;
 int opt_index;
 int output = OUTPUT_MESSAGES;
-int exclude = EXCLUDE_true;
+bool exclude = true;
 const char **search_exclude_tags = NULL;
 size_t search_exclude_tags_length = 0;
 bool batch = false;
@@ -175,10 +169,7 @@ notmuch_count_command (notmuch_config_t *config, int argc, 
char *argv[])
  { "messages", OUTPUT_MESSAGES },
  { "files", OUTPUT_FILES },
  { 0, 0 } } },
-   { .opt_keyword = &exclude, .name = "exclude", .keywords =
- (notmuch_keyword_t []){ { "true", EXCLUDE_true },
- { "false", EXCLUDE_false },
- { 0, 0 } } },
+   { .opt_bool = &exclude, .name = "exclude" },
{ .opt_bool = &print_lastmod, .name = "lastmod" },
{ .opt_bool = &batch, .name = "batch" },
{ .opt_string = &input_file_name, .name = "input" },
@@ -221,7 +212,7 @@ notmuch_count_command (notmuch_config_t *config, int argc, 
char *argv[])
return EXIT_FAILURE;
 }
 
-if (exclude == EXCLUDE_true) {
+if (exclude) {
search_exclude_tags = notmuch_config_get_search_exclude_tags
(config, &search_exclude_tags_length);
 }
-- 
2.11.0

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


Re: [bug] [emacs] notmuch-show: names not shown on some mailing lists

2017-10-14 Thread William Casarin
Nevermind, the issue seems to go away on master

Sorry for the noise...
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH 1/3] cli: add support for --no- prefixed boolean and keyword flag arguments

2017-10-14 Thread William Casarin

Hey Jani,

Patches look good so far, concept ack for sure.


Jani Nikula  writes:

> For example, you can use --no-exclude instead of --exclude=false in
> notmuch show. If we had keyword flag arguments with some flags
> defaulting to on, say --include=tags in notmuch dump/restore, this
> would allow --no-include=tags to switch that off while not affecting
> other flags.

I've been testing it a bit, I can't seem to make this work in this example:

./notmuch count --no-exclude

After some brief investigation it might be because count is using
EXCLUDE_true(1) and EXCLUDE_false(0) which are not equal to
NOTMUCH_EXCLUDE_TRUE(1) and NOTMUCH_EXCLUDE_FALSE(2), but I'm not sure.

Cheers,
William

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


Re: [bug] [emacs] notmuch-show: names not shown on some mailing lists

2017-10-14 Thread William Casarin
William Casarin  writes:

> Jani Nikula  writes:
>
>> The information comes from the cli. Can you reproduce this using some
>> notmuch show --format=sexp --body=false query, perhaps on just one of
>> the messages?
>
> I get this:
>
>   :From "Person via Mailing list\011"

If I wanted to fix this, would I implement it as a wash function?

Thanks,


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


Re: [bug] [emacs] notmuch-show: names not shown on some mailing lists

2017-10-14 Thread William Casarin
Jani Nikula  writes:

> The information comes from the cli. Can you reproduce this using some
> notmuch show --format=sexp --body=false query, perhaps on just one of
> the messages?

I get this:

  :From "Person via Mailing list\011"


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


Re: [PATCH v3 11/15] config: indexing defaults will be stored in the database.

2017-10-14 Thread David Bremner
Daniel Kahn Gillmor  writes:


>  static int
>  _print_db_config(notmuch_config_t *config, const char *name)
> @@ -859,6 +860,8 @@ notmuch_config_command_get (notmuch_config_t *config, 
> char *item)
>   notmuch_built_with (item + strlen (BUILT_WITH_PREFIX)) ? "true" 
> : "false");
>  } else if (STRNCMP_LITERAL (item, QUERY_PREFIX) == 0) {
>   return _print_db_config (config, item);
> +} else if (STRNCMP_LITERAL (item, INDEX_PREFIX) == 0) {
> + return _print_db_config (config, item);
>  } else {
>   char **value;

I wonder if we should sanity check the value of 'item' more here. With
'query.', it makes sense to get or set anything, since it's just the
name of a stored query. With 'index.', presumably only certain
parameters make sense. As a motivating example, consider someone who
sets

$ notmuch config index.try_decrypt true

then changes their mind

$ notmuch config index.try-decrypt false

They _think_ they are safe, but notmuch is silently going to continue
decrypting their mail
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v3 09/15] gmime-extra: drop compat layer for g_mime_multipart_encrypted_decrypt

2017-10-14 Thread David Bremner
Daniel Kahn Gillmor  writes:

> In practice, we're going to see this function invoked differently
> depending on which gmime we build against.  The compatibility layer
> forces our code into the lowest-common-denominator -- unable to make
> use of new features even when built against a newer version.
>
> Dropping the compatibility layer paves the way for clearer use of
> features from GMime 3.0 in future commits.

pushed patches 7 and 9.

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


[PATCH 2/3] cli: use the negating boolean support for new and insert --no-hooks

2017-10-14 Thread Jani Nikula
This lets us use the positive hooks variable in code, increasing
clarity.
---
 notmuch-insert.c | 6 +++---
 notmuch-new.c| 8 
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/notmuch-insert.c b/notmuch-insert.c
index 32be74193472..6878313e188f 100644
--- a/notmuch-insert.c
+++ b/notmuch-insert.c
@@ -455,7 +455,7 @@ notmuch_insert_command (notmuch_config_t *config, int argc, 
char *argv[])
 const char *folder = "";
 bool create_folder = false;
 bool keep = false;
-bool no_hooks = false;
+bool hooks = true;
 bool synchronize_flags;
 char *maildir;
 char *newpath;
@@ -466,7 +466,7 @@ notmuch_insert_command (notmuch_config_t *config, int argc, 
char *argv[])
{ .opt_string = &folder, .name = "folder" },
{ .opt_bool = &create_folder, .name = "create-folder" },
{ .opt_bool = &keep, .name = "keep" },
-   { .opt_bool =  &no_hooks, .name = "no-hooks" },
+   { .opt_bool = &hooks, .name = "hooks" },
{ .opt_inherit = notmuch_shared_options },
{ }
 };
@@ -573,7 +573,7 @@ notmuch_insert_command (notmuch_config_t *config, int argc, 
char *argv[])
}
 }
 
-if (! no_hooks && status == NOTMUCH_STATUS_SUCCESS) {
+if (hooks && status == NOTMUCH_STATUS_SUCCESS) {
/* Ignore hook failures. */
notmuch_run_hook (db_path, "post-insert");
 }
diff --git a/notmuch-new.c b/notmuch-new.c
index 0f50457eb894..a6ca484101ce 100644
--- a/notmuch-new.c
+++ b/notmuch-new.c
@@ -954,7 +954,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, 
char *argv[])
 int opt_index;
 unsigned int i;
 bool timer_is_active = false;
-bool no_hooks = false;
+bool hooks = true;
 bool quiet = false, verbose = false;
 notmuch_status_t status;
 
@@ -962,7 +962,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, 
char *argv[])
{ .opt_bool = &quiet, .name = "quiet" },
{ .opt_bool = &verbose, .name = "verbose" },
{ .opt_bool = &add_files_state.debug, .name = "debug" },
-   { .opt_bool = &no_hooks, .name = "no-hooks" },
+   { .opt_bool = &hooks, .name = "hooks" },
{ .opt_inherit = notmuch_shared_options },
{ }
 };
@@ -995,7 +995,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, 
char *argv[])
}
 }
 
-if (!no_hooks) {
+if (hooks) {
ret = notmuch_run_hook (db_path, "pre-new");
if (ret)
return EXIT_FAILURE;
@@ -1160,7 +1160,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, 
char *argv[])
 
 notmuch_database_destroy (notmuch);
 
-if (!no_hooks && !ret && !interrupted)
+if (hooks && !ret && !interrupted)
ret = notmuch_run_hook (db_path, "post-new");
 
 if (ret || interrupted)
-- 
2.11.0

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


[PATCH 2/2] test: test regexp based new.ignore

2017-10-14 Thread Jani Nikula
Just some basics.
---
 test/T050-new.sh | 22 ++
 1 file changed, 22 insertions(+)

diff --git a/test/T050-new.sh b/test/T050-new.sh
index 272ed417aa2e..ee9ce08c8e86 100755
--- a/test/T050-new.sh
+++ b/test/T050-new.sh
@@ -259,6 +259,28 @@ ln -s i_do_not_exist "${MAIL_DIR}"/broken_link
 output=$(NOTMUCH_NEW 2>&1)
 test_expect_equal "$output" "No new mail."
 
+test_begin_subtest "Ignore files and directories specified in new.ignore 
(regexp)"
+notmuch config set new.ignore ".git" "/^bro.*ink\$/" "/ignored.*file/"
+output=$(NOTMUCH_NEW --debug 2>&1 | sort)
+test_expect_equal "$output" \
+"(D) add_files, pass 1: explicitly ignoring ${MAIL_DIR}/.git
+(D) add_files, pass 1: explicitly ignoring ${MAIL_DIR}/.ignored_hidden_file
+(D) add_files, pass 1: explicitly ignoring ${MAIL_DIR}/broken_link
+(D) add_files, pass 1: explicitly ignoring ${MAIL_DIR}/ignored_file
+(D) add_files, pass 1: explicitly ignoring ${MAIL_DIR}/one/ignored_file
+(D) add_files, pass 1: explicitly ignoring ${MAIL_DIR}/one/two/ignored_file
+(D) add_files, pass 1: explicitly ignoring ${MAIL_DIR}/one/two/three/.git
+(D) add_files, pass 1: explicitly ignoring 
${MAIL_DIR}/one/two/three/ignored_file
+(D) add_files, pass 2: explicitly ignoring ${MAIL_DIR}/.git
+(D) add_files, pass 2: explicitly ignoring ${MAIL_DIR}/.ignored_hidden_file
+(D) add_files, pass 2: explicitly ignoring ${MAIL_DIR}/broken_link
+(D) add_files, pass 2: explicitly ignoring ${MAIL_DIR}/ignored_file
+(D) add_files, pass 2: explicitly ignoring ${MAIL_DIR}/one/ignored_file
+(D) add_files, pass 2: explicitly ignoring ${MAIL_DIR}/one/two/ignored_file
+(D) add_files, pass 2: explicitly ignoring ${MAIL_DIR}/one/two/three/.git
+(D) add_files, pass 2: explicitly ignoring 
${MAIL_DIR}/one/two/three/ignored_file
+No new mail."
+
 test_begin_subtest "Quiet: No new mail."
 output=$(NOTMUCH_NEW --quiet)
 test_expect_equal "$output" ""
-- 
2.11.0

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


[PATCH 3/3] test: expand argument parsing sanity checks

2017-10-14 Thread Jani Nikula
Test the various boolean formats and --no- prefixed boolean and
keyword flag arguments.
---
 test/T410-argument-parsing.sh | 28 
 1 file changed, 28 insertions(+)

diff --git a/test/T410-argument-parsing.sh b/test/T410-argument-parsing.sh
index 4a2b25c6486d..243d0241b9b6 100755
--- a/test/T410-argument-parsing.sh
+++ b/test/T410-argument-parsing.sh
@@ -37,4 +37,32 @@ positional arg 1 false
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest "--boolean=true"
+$TEST_DIRECTORY/arg-test --boolean=true > OUTPUT
+cat < EXPECTED
+boolean 1
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "--boolean=false"
+$TEST_DIRECTORY/arg-test --boolean=false > OUTPUT
+cat < EXPECTED
+boolean 0
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "--no-boolean"
+$TEST_DIRECTORY/arg-test --no-boolean > OUTPUT
+cat < EXPECTED
+boolean 0
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "--no-flag"
+$TEST_DIRECTORY/arg-test --flag=one --flag=three --no-flag=three > OUTPUT
+cat < EXPECTED
+flags 1
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
 test_done
-- 
2.11.0

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


[PATCH 1/2] cli/new: support // in new.ignore

2017-10-14 Thread Jani Nikula
Add support for using // style regular expressions in
new.ignore, mixed with the old style verbatim file and directory
basenames. The regex is matched against the relative path from the
database path.
---
 doc/man1/notmuch-config.rst |  21 +--
 notmuch-new.c   | 134 
 2 files changed, 140 insertions(+), 15 deletions(-)

diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst
index 6a51e64f1517..0af86f9beee4 100644
--- a/doc/man1/notmuch-config.rst
+++ b/doc/man1/notmuch-config.rst
@@ -75,11 +75,22 @@ The available configuration items are described below.
 Default: ``unread;inbox``.
 
 **new.ignore**
-A list of file and directory names, without path, that will not
-be searched for messages by **notmuch new**. All the files and
-directories matching any of the names specified here will be
-ignored, regardless of the location in the mail store directory
-hierarchy.
+A list to specify files and directories that will not be
+searched for messages by **notmuch new**. Each entry in the
+list is either:
+
+  A file or a directory name, without path, that will be
+  ignored, regardless of the location in the mail store
+  directory hierarchy.
+
+Or:
+
+  A regular expression delimited with // that will be matched
+  against the path of the file or directory relative to the
+  database path. Matching files and directories will be
+  ignored. The beginning and end of string must be explictly
+  anchored. For example, /.*/foo$/ would match "bar/foo" and
+  "bar/baz/foo", but not "foo" or "bar/foobar".
 
 Default: empty list.
 
diff --git a/notmuch-new.c b/notmuch-new.c
index 0f50457eb894..5a262e419b44 100644
--- a/notmuch-new.c
+++ b/notmuch-new.c
@@ -42,13 +42,17 @@ enum verbosity {
 };
 
 typedef struct {
+const char *db_path;
+
 int output_is_a_tty;
 enum verbosity verbosity;
 bool debug;
 const char **new_tags;
 size_t new_tags_length;
-const char **new_ignore;
-size_t new_ignore_length;
+const char **ignore_verbatim;
+size_t ignore_verbatim_length;
+regex_t *ignore_regex;
+size_t ignore_regex_length;
 
 int total_files;
 int processed_files;
@@ -240,18 +244,125 @@ _special_directory (const char *entry)
 return strcmp (entry, ".") == 0 || strcmp (entry, "..") == 0;
 }
 
+static bool
+_setup_ignore (notmuch_config_t *config, add_files_state_t *state)
+{
+const char **ignore_list, **ignore;
+int nregex = 0, nverbatim = 0;
+const char **verbatim = NULL;
+regex_t *regex = NULL;
+
+ignore_list = notmuch_config_get_new_ignore (config, NULL);
+if (! ignore_list)
+   return true;
+
+for (ignore = ignore_list; *ignore; ignore++) {
+   const char *s = *ignore;
+   size_t len = strlen (s);
+
+   if (len == 0) {
+   fprintf (stderr, "Error: Empty string in new.ignore list\n");
+   return false;
+   }
+
+   if (s[0] == '/') {
+   regex_t *preg;
+   char *r;
+   int rerr;
+
+   if (len < 3 || s[len - 1] != '/') {
+   fprintf (stderr, "Error: Malformed pattern '%s' in 
new.ignore\n",
+s);
+   return false;
+   }
+
+   r = talloc_strndup (config, s + 1, len - 2);
+   regex = talloc_realloc (config, regex, regex_t, nregex + 1);
+   preg = ®ex[nregex];
+
+   rerr = regcomp (preg, r, REG_EXTENDED | REG_NOSUB);
+   if (rerr) {
+   size_t error_size = regerror (rerr, preg, NULL, 0);
+   char *error = talloc_size (r, error_size);
+
+   regerror (rerr, preg, error, error_size);
+
+   fprintf (stderr, "Error: Invalid regex '%s' in new.ignore: 
%s\n",
+r, error);
+   return false;
+   }
+   nregex++;
+
+   talloc_free (r);
+   } else {
+   verbatim = talloc_realloc (config, verbatim, const char *,
+  nverbatim + 1);
+   verbatim[nverbatim++] = s;
+   }
+}
+
+state->ignore_regex = regex;
+state->ignore_regex_length = nregex;
+state->ignore_verbatim = verbatim;
+state->ignore_verbatim_length = nverbatim;
+
+return true;
+}
+
+static char *
+_get_relative_path (const char *db_path, const char *dirpath, const char 
*entry)
+{
+size_t db_path_len = strlen (db_path);
+
+/* paranoia? */
+if (strncmp (dirpath, db_path, db_path_len) != 0) {
+   fprintf (stderr, "Warning: '%s' is not a subdirectory of '%s'\n",
+dirpath, db_path);
+   return NULL;
+}
+
+dirpath += db_path_len;
+while (*dirpath == '/')
+   dirpath++;
+
+if (*dirpath)
+   return talloc_asprintf (NULL, "%s/%s", dirpath, entry);
+else
+   return 

[PATCH 1/3] cli: add support for --no- prefixed boolean and keyword flag arguments

2017-10-14 Thread Jani Nikula
Add transparent support for negating boolean and keyword flag
arguments using --no-argument style on the command line. That is, if
the option description contains a boolean or a keyword flag argument
named "argument", --no-argument will match and negate it.

For boolean arguments this obviously means the logical NOT. For
keyword flag arguments this means bitwise AND of the bitwise NOT,
i.e. masking out the specified bits instead of OR'ing them in.

For example, you can use --no-exclude instead of --exclude=false in
notmuch show. If we had keyword flag arguments with some flags
defaulting to on, say --include=tags in notmuch dump/restore, this
would allow --no-include=tags to switch that off while not affecting
other flags.

As a curiosity, you should be able to warp your brain using
--no-exclude=true meaning false and --no-exclude=false meaning true if
you wish.

Specifying both "argument" and "no-argument" style arguments in the
same option description should be avoided. In this case, --no-argument
would match whichever is specified first, and --argument would only
match "argument".
---
 command-line-arguments.c | 48 +---
 1 file changed, 37 insertions(+), 11 deletions(-)

diff --git a/command-line-arguments.c b/command-line-arguments.c
index 1ff5aae578c6..69ee1cb07f47 100644
--- a/command-line-arguments.c
+++ b/command-line-arguments.c
@@ -11,8 +11,9 @@
 */
 
 static bool
-_process_keyword_arg (const notmuch_opt_desc_t *arg_desc, char next, const 
char *arg_str) {
-
+_process_keyword_arg (const notmuch_opt_desc_t *arg_desc, char next,
+ const char *arg_str, bool negate)
+{
 const notmuch_keyword_t *keywords;
 
 if (next == '\0') {
@@ -24,7 +25,9 @@ _process_keyword_arg (const notmuch_opt_desc_t *arg_desc, 
char next, const char
if (strcmp (arg_str, keywords->name) != 0)
continue;
 
-   if (arg_desc->opt_flags)
+   if (arg_desc->opt_flags && negate)
+   *arg_desc->opt_flags &= ~keywords->value;
+   else if (arg_desc->opt_flags)
*arg_desc->opt_flags |= keywords->value;
else
*arg_desc->opt_keyword = keywords->value;
@@ -39,7 +42,9 @@ _process_keyword_arg (const notmuch_opt_desc_t *arg_desc, 
char next, const char
 }
 
 static bool
-_process_boolean_arg (const notmuch_opt_desc_t *arg_desc, char next, const 
char *arg_str) {
+_process_boolean_arg (const notmuch_opt_desc_t *arg_desc, char next,
+ const char *arg_str, bool negate)
+{
 bool value;
 
 if (next == '\0' || strcmp (arg_str, "true") == 0) {
@@ -51,7 +56,7 @@ _process_boolean_arg (const notmuch_opt_desc_t *arg_desc, 
char next, const char
return false;
 }
 
-*arg_desc->opt_bool = value;
+*arg_desc->opt_bool = negate ? !value : value;
 
 return true;
 }
@@ -139,6 +144,8 @@ parse_position_arg (const char *arg_str, int pos_arg_index,
 return false;
 }
 
+#define NEGATIVE_PREFIX "no-"
+
 /*
  * Search for a non-positional (i.e. starting with --) argument matching arg,
  * parse a possible value, and assign to *output_var
@@ -155,6 +162,14 @@ parse_option (int argc, char **argv, const 
notmuch_opt_desc_t *options, int opt_
 assert(options);
 
 const char *arg = _arg + 2; /* _arg starts with -- */
+const char *negative_arg = NULL;
+
+/* See if this is a --no-argument */
+if (strlen (arg) > strlen (NEGATIVE_PREFIX) &&
+   strncmp (arg, NEGATIVE_PREFIX, strlen (NEGATIVE_PREFIX)) == 0) {
+   negative_arg = arg + strlen (NEGATIVE_PREFIX);
+}
+
 const notmuch_opt_desc_t *try;
 
 const char *next_arg = NULL;
@@ -171,11 +186,22 @@ parse_option (int argc, char **argv, const 
notmuch_opt_desc_t *options, int opt_
if (! try->name)
continue;
 
-   if (strncmp (arg, try->name, strlen (try->name)) != 0)
+   char next;
+   const char *value;
+   bool negate = false;
+
+   if (strncmp (arg, try->name, strlen (try->name)) == 0) {
+   next = arg[strlen (try->name)];
+   value = arg + strlen (try->name) + 1;
+   } else if (negative_arg && (try->opt_bool || try->opt_flags) &&
+  strncmp (negative_arg, try->name, strlen (try->name)) == 0) {
+   next = negative_arg[strlen (try->name)];
+   value = negative_arg + strlen (try->name) + 1;
+   /* The argument part of --no-argument matches, negate the result. */
+   negate = true;
+   } else {
continue;
-
-   char next = arg[strlen (try->name)];
-   const char *value = arg + strlen(try->name) + 1;
+   }
 
/*
 * If we have not reached the end of the argument (i.e. the
@@ -194,9 +220,9 @@ parse_option (int argc, char **argv, const 
notmuch_opt_desc_t *options, int opt_
 
bool opt_status = false;
if (try->opt_keyword || try->opt_flags)
-   opt_status = _process_keyword_arg (try, next, value);
+   opt_status = _process_k

[PATCH] cli: allow empty strings for notmuch insert --folder argument

2017-10-14 Thread Jani Nikula
Now that it's easy to add argument specific modifiers in opt
descriptions, add a new .allow_empty field to allow empty strings for
individual string arguments while retaining strict checks
elsewhere. Use this for notmuch insert --folder, where the empty
string means top level folder.

---

This patch addresses id:87y3owr22c@nikula.org
---
 command-line-arguments.c| 2 +-
 command-line-arguments.h| 3 +++
 doc/man1/notmuch-insert.rst | 3 ++-
 notmuch-insert.c| 2 +-
 4 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/command-line-arguments.c b/command-line-arguments.c
index 1ff5aae578c6..db73ca5efb89 100644
--- a/command-line-arguments.c
+++ b/command-line-arguments.c
@@ -81,7 +81,7 @@ _process_string_arg (const notmuch_opt_desc_t *arg_desc, char 
next, const char *
fprintf (stderr, "Option \"%s\" needs a string argument.\n", 
arg_desc->name);
return false;
 }
-if (arg_str[0] == '\0') {
+if (arg_str[0] == '\0' && ! arg_desc->allow_empty) {
fprintf (stderr, "String argument for option \"%s\" must be 
non-empty.\n", arg_desc->name);
return false;
 }
diff --git a/command-line-arguments.h b/command-line-arguments.h
index 76ca4dcbb276..c0228f7cb634 100644
--- a/command-line-arguments.h
+++ b/command-line-arguments.h
@@ -32,6 +32,9 @@ typedef struct notmuch_opt_desc {
 /* Optional, if non-NULL, set to true if the option is present. */
 bool *present;
 
+/* Optional, allow empty strings for opt_string. */
+bool allow_empty;
+
 /* Must be set for opt_keyword and opt_flags. */
 const struct notmuch_keyword *keywords;
 } notmuch_opt_desc_t;
diff --git a/doc/man1/notmuch-insert.rst b/doc/man1/notmuch-insert.rst
index f79600d6571f..2f2466a6588b 100644
--- a/doc/man1/notmuch-insert.rst
+++ b/doc/man1/notmuch-insert.rst
@@ -34,7 +34,8 @@ Supported options for **insert** include
 ``--folder=<``\ folder\ **>**
 Deliver the message to the specified folder, relative to the
 top-level directory given by the value of **database.path**. The
-default is to deliver to the top-level directory.
+default is the empty string, which means delivering to the
+top-level directory.
 
 ``--create-folder``
 Try to create the folder named by the ``--folder`` option, if it
diff --git a/notmuch-insert.c b/notmuch-insert.c
index 32be74193472..cff74731 100644
--- a/notmuch-insert.c
+++ b/notmuch-insert.c
@@ -463,7 +463,7 @@ notmuch_insert_command (notmuch_config_t *config, int argc, 
char *argv[])
 unsigned int i;
 
 notmuch_opt_desc_t options[] = {
-   { .opt_string = &folder, .name = "folder" },
+   { .opt_string = &folder, .name = "folder", .allow_empty = true },
{ .opt_bool = &create_folder, .name = "create-folder" },
{ .opt_bool = &keep, .name = "keep" },
{ .opt_bool =  &no_hooks, .name = "no-hooks" },
-- 
2.11.0

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


Re: [PATCH 0/3] nmbug:

2017-10-14 Thread Jani Nikula
On Tue, 10 Oct 2017, "W. Trevor King"  wrote:
> I expect the best time to make that bump is just before we cut our
> next notmuch release.  Once we do, we can update the wiki page [1] to
> suggest nmbug 0.3+ and remove the checkout step from “Getting
> started”.

Could we add a "nmbug required version" metadata to the repository, and
add a version check in nmbug? Obviously this doesn't help with 0.2, but,
if we added this at 0.3, we could require a new nmbug version on the
clients.

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


Re: [bug] [emacs] notmuch-show: names not shown on some mailing lists

2017-10-14 Thread Jani Nikula
On Tue, 10 Oct 2017, William Casarin  wrote:
> Hey there,
>
> Here's something I've noticed in some mailing list threads in notmuch-show:
>
>   https://jb55.com/s/81d7c740ef60984d.png
>
> It doesn't look like it is showing the correct name.
> Looking at the raw message, the From line looks like this:
>
>   From: Some Person via Some-mailinglist
> 
>   Reply-To: Some Person 
>
> Any way to handle these situations gracefully? Somehow show
> `Some Person` instead of `somemailingl...@lists.linuxfoundation.org`?

The information comes from the cli. Can you reproduce this using some
notmuch show --format=sexp --body=false query, perhaps on just one of
the messages?

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


Re: [PATCH v2 04/10] index: implement notmuch_indexopts_t with try_decrypt

2017-10-14 Thread Jani Nikula
On Mon, 09 Oct 2017, Daniel Kahn Gillmor  wrote:
> On Sat 2017-09-23 19:10:18 +0300, Jani Nikula wrote:
>>> --- a/lib/indexopts.c
>>> +++ b/lib/indexopts.c
>>> @@ -21,9 +21,27 @@
>>>  #include "notmuch-private.h"
>>>  
>>>  notmuch_indexopts_t *
>>> -notmuch_database_get_default_indexopts (notmuch_database_t unused (*db))
>>> +notmuch_database_get_default_indexopts (notmuch_database_t *db)
>>>  {
>>> -return NULL;
>>> +return talloc_zero (db, notmuch_indexopts_t);
>>
>> I wonder about the lifetime of indexopts. Should default indexopts be
>> part of the db object, so that your caller above doesn't have to
>> alloc/destroy it for every file?
>
> The caller doesn't have to alloc/destroy it for every file, they can
> alloc it once and pass it in for every file.

My point was, if the caller doesn't do it, it'll get alloced and
destroyed per file.

BR,
Jani.


>
> I'd rather not have the indexopts be part of the db object itself
> explicitly, because i can imagine a longer-running program wanting to
> create two indexopts objects, configuring them differently, and re-using
> one or the other without wanting to modify the database itself.
>
>> Our library interface has a leaky abstraction of the talloc hierarchical
>> refcounting. We don't talk about it in any of the docs, some of it is
>> implied, most of it is completely surprising if the library interface
>> user assumes a traditional C memory allocation model without
>> refcounting.
>
> right, probably the most surprising thing would be if the user got a
> default indexopts from one database object, and then tried to apply it
> to another database object, after having deleted the first database
> object.
>
> However, we *do* talk about it in the docs now, so i think we've given
> fair warning:
>
> -
> /**
>  * get the current default indexing options for a given database.
>  *
>  * This object will survive until the database itself is destroyed,
>  * but the caller may also release it earlier with
>  * notmuch_indexopts_destroy.
>  *
>  * This object represents a set of options on how a message can be
>  * added to the index.  At the moment it is a featureless stub.
>  *
>  * @since libnotmuch 5.1 (notmuch 0.26)
>  */
> notmuch_indexopts_t *
> notmuch_database_get_default_indexopts (notmuch_database_t *db);
> -
>
> --dkg
___
notmuch mailing list
notmuch@notmuchmail.org
https://notmuchmail.org/mailman/listinfo/notmuch


Re: [PATCH v3 10/15] crypto: index encrypted parts when indexopts try_decrypt is set.

2017-10-14 Thread David Bremner
Daniel Kahn Gillmor  writes:

>
> "index.auto.decryption" has at least two different meanings at first
> blush.  For example, it might mean "this message was automatically
> decrypted", which isn't necessarily the case.

I think in either case you need to document what the properties
mean. I don't think "index-decryption" by itself is unambiguous either.

> second confusion:
>
> Even if we used a less ambiguous term like "flush-on-reindex", it would
> seem weird to need to say "notmuch search
> property:index.flush-on-reindex.decryption=failure",

Yes, you seem to envision interactive searching for these
properities. I'll have to take your word for it. I tend to think about
properties as more like APIs than as end user facing.

> We could maybe avoid both of these confusions by dropping "auto"
> altogether and defining the following rule:
>
>  * properties prefixed with "index." are set during indexing and will be
>cleared (and possibly re-set) upon reindexing
>
> wdyt?
>
> If you're ok with that, i could try to update the series to work that
> way (and include documentation along those lines).

I don't object to this.

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