Merge branch 'release'

Conflicts:
        doc/man1/notmuch-reply.rst
	doc/man1/notmuch-show.rst

Conflicts taken from release (dkg's doc changes)
This commit is contained in:
David Bremner 2017-12-31 09:26:13 -04:00
commit 99407db25c
20 changed files with 230 additions and 80 deletions

59
NEWS
View file

@ -24,6 +24,17 @@ Support for re-indexing existing messages
archive, the recorded Subject: of may change upon reindexing,
depending on the order in which the variants are indexed.
Improved error reporting in notmuch new
Give more details when reporting certain Xapian exceptions.
Support maildir synced tags in `new.tags`
Tags `draft`, `flagged`, `passed`, and `replied` are now supported
in `new.tags`. The tag `unread` is still special in the presence of
maildir syncing, and will be added for files in `new/` regardless of
the setting of `new.tags`.
Encrypted Mail
--------------
@ -41,6 +52,54 @@ Indexing cleartext of encrypted e-mails
that the notmuch index itself is adequately protected. DO NOT USE
this feature without considering the security of your index.
Library Changes
---------------
Indexing files with duplicate message-id
Files with duplicate message-id's are now indexed, and searchable
via terms and phrases. There are known issues related to
presentation of results and regular-expression search, but in
principle no mail file should be completely unsearchable now.
New functions to count files
Two new functions in the libnotmuch API:
`notmuch_message_count_files`, and `notmuch_thread_get_total_files`.
Change of return value of `notmuch_thread_get_authors`
In certain corner cases, `notmuch_thread_get_authors` previously
returned NULL. This has been replaced by an empty string, since the
possibility of NULL was not documented.
Python Bindings
---------------
Python bindings specific Debian packaging is removed
The bindings have been build by the top level Debian packaging for a
long time, and `bindings/python/debian` has bit-rotted.
Open mail files in binary mode when using Python 3
This avoids certain encoding related crashes under Python 3.
Add python bindings for notmuch_database_{get,set}_config*
nmbug
-----
nmbug's internal version increases to 0.3 in this notmuch release.
User-facing changes with this notmuch release:
* Accept failures to unset `core.worktree` in `clone`, which allows
nmbug to be used with Git 2.11.0 and later.
* Auto-checkout in `clone` if it wouldn't clobber existing content,
which makes the initial clone more convenient.
* Only error for invalid diff lines in `tags/`, which allows for
`README`s and similar in nmbug repositories.
Notmuch 0.25.3 (2017-12-08)
===========================

View file

@ -1,3 +1,3 @@
# this file should be kept in sync with ../../../version
__VERSION__ = '0.26~rc0'
__VERSION__ = '0.26~rc1'
SOVERSION = '5'

View file

@ -4,13 +4,19 @@
#include "error_util.h"
#include "command-line-arguments.h"
typedef enum {
OPT_FAILED, /* false */
OPT_OK, /* good */
OPT_GIVEBACK, /* pop one of the arguments you thought you were getting off the stack */
} opt_handled;
/*
Search the array of keywords for a given argument, assigning the
output variable to the corresponding value. Return false if nothing
matches.
*/
static bool
static opt_handled
_process_keyword_arg (const notmuch_opt_desc_t *arg_desc, char next,
const char *arg_str, bool negate)
{
@ -32,16 +38,32 @@ _process_keyword_arg (const notmuch_opt_desc_t *arg_desc, char next,
else
*arg_desc->opt_keyword = keywords->value;
return true;
return OPT_OK;
}
if (arg_desc->opt_keyword && arg_desc->keyword_no_arg_value && next != ':' && next != '=') {
for (keywords = arg_desc->keywords; keywords->name; keywords++) {
if (strcmp (arg_desc->keyword_no_arg_value, keywords->name) != 0)
continue;
*arg_desc->opt_keyword = keywords->value;
fprintf (stderr, "Warning: No known keyword option given for \"%s\", choosing value \"%s\"."
" Please specify the argument explicitly!\n", arg_desc->name, arg_desc->keyword_no_arg_value);
return OPT_GIVEBACK;
}
fprintf (stderr, "No matching keyword for option \"%s\" and default value \"%s\" is invalid.\n", arg_str, arg_desc->name);
return OPT_FAILED;
}
if (next != '\0')
fprintf (stderr, "Unknown keyword argument \"%s\" for option \"%s\".\n", arg_str, arg_desc->name);
else
fprintf (stderr, "Option \"%s\" needs a keyword argument.\n", arg_desc->name);
return false;
return OPT_FAILED;
}
static bool
static opt_handled
_process_boolean_arg (const notmuch_opt_desc_t *arg_desc, char next,
const char *arg_str, bool negate)
{
@ -53,45 +75,45 @@ _process_boolean_arg (const notmuch_opt_desc_t *arg_desc, char next,
value = false;
} else {
fprintf (stderr, "Unknown argument \"%s\" for (boolean) option \"%s\".\n", arg_str, arg_desc->name);
return false;
return OPT_FAILED;
}
*arg_desc->opt_bool = negate ? !value : value;
return true;
return OPT_OK;
}
static bool
static opt_handled
_process_int_arg (const notmuch_opt_desc_t *arg_desc, char next, const char *arg_str) {
char *endptr;
if (next == '\0' || arg_str[0] == '\0') {
fprintf (stderr, "Option \"%s\" needs an integer argument.\n", arg_desc->name);
return false;
return OPT_FAILED;
}
*arg_desc->opt_int = strtol (arg_str, &endptr, 10);
if (*endptr == '\0')
return true;
return OPT_OK;
fprintf (stderr, "Unable to parse argument \"%s\" for option \"%s\" as an integer.\n",
arg_str, arg_desc->name);
return false;
return OPT_FAILED;
}
static bool
static opt_handled
_process_string_arg (const notmuch_opt_desc_t *arg_desc, char next, const char *arg_str) {
if (next == '\0') {
fprintf (stderr, "Option \"%s\" needs a string argument.\n", arg_desc->name);
return false;
return OPT_FAILED;
}
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;
return OPT_FAILED;
}
*arg_desc->opt_string = arg_str;
return true;
return OPT_OK;
}
/* Return number of non-NULL opt_* fields in opt_desc. */
@ -212,13 +234,15 @@ parse_option (int argc, char **argv, const notmuch_opt_desc_t *options, int opt_
if (next != '=' && next != ':' && next != '\0')
continue;
if (next == '\0' && next_arg != NULL && ! try->opt_bool) {
bool lookahead = (next == '\0' && next_arg != NULL && ! try->opt_bool);
if (lookahead) {
next = ' ';
value = next_arg;
opt_index ++;
}
bool opt_status = false;
opt_handled opt_status = OPT_FAILED;
if (try->opt_keyword || try->opt_flags)
opt_status = _process_keyword_arg (try, next, value, negate);
else if (try->opt_bool)
@ -230,9 +254,12 @@ parse_option (int argc, char **argv, const notmuch_opt_desc_t *options, int opt_
else
INTERNAL_ERROR ("unknown or unhandled option \"%s\"", try->name);
if (! opt_status)
if (opt_status == OPT_FAILED)
return -1;
if (lookahead && opt_status == OPT_GIVEBACK)
opt_index --;
if (try->present)
*try->present = true;

View file

@ -26,6 +26,10 @@ typedef struct notmuch_opt_desc {
const char **opt_string;
const char **opt_position;
/* for opt_keyword only: if no matching arguments were found, and
* keyword_no_arg_value is set, then use keyword_no_arg_value instead. */
const char *keyword_no_arg_value;
/* Must be set except for opt_inherit and opt_position. */
const char *name;

View file

@ -351,7 +351,7 @@ _notmuch_reply()
return
;;
--decrypt)
COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
COMPREPLY=( $( compgen -W "true auto false" -- "${cur}" ) )
return
;;
esac
@ -517,10 +517,14 @@ _notmuch_show()
COMPREPLY=( $( compgen -W "text json sexp mbox raw" -- "${cur}" ) )
return
;;
--exclude|--body|--decrypt)
--exclude|--body)
COMPREPLY=( $( compgen -W "true false" -- "${cur}" ) )
return
;;
--decrypt)
COMPREPLY=( $( compgen -W "true auto false" -- "${cur}" ) )
return
;;
esac
! $split &&

6
debian/changelog vendored
View file

@ -1,3 +1,9 @@
notmuch (0.26~rc1-1) experimental; urgency=medium
* Second upstream release candidate
-- David Bremner <bremner@debian.org> Fri, 29 Dec 2017 16:49:37 -0400
notmuch (0.26~rc0-1) experimental; urgency=medium
* Upstream release candidate

View file

@ -51,7 +51,7 @@ except ImportError: # Python 2
from urllib import unquote as _unquote
__version__ = '0.2'
__version__ = '0.3'
_LOG = _logging.getLogger('nmbug')
_LOG.setLevel(_logging.WARNING)

View file

@ -70,20 +70,26 @@ Supported options for **reply** include
order, and copy values from the first that contains something
other than only the user's addresses.
``--decrypt``
Decrypt any MIME encrypted parts found in the selected content
(ie. "multipart/encrypted" parts). Status of the decryption will
be reported (currently only supported with --format=json and
--format=sexp) and on successful decryption the
multipart/encrypted part will be replaced by the decrypted
content.
``--decrypt=(false|auto|true)``
If a session key is already known for the message, then it will be
decrypted automatically unless the user explicitly sets
``--decrypt=false``.
If ``true``, decrypt any MIME encrypted parts found in the
selected content (i.e., "multipart/encrypted" parts). Status
of the decryption will be reported (currently only supported
with --format=json and --format=sexp), and on successful
decryption the multipart/encrypted part will be replaced by
the decrypted content.
Decryption expects a functioning **gpg-agent(1)** to provide any
needed credentials. Without one, the decryption will likely fail.
If ``auto``, and a session key is already known for the
message, then it will be decrypted, but notmuch will not try
to access the user's secret keys.
Use ``false`` to avoid even automatic decryption.
Non-automatic decryption expects a functioning
**gpg-agent(1)** to provide any needed credentials. Without
one, the decryption will likely fail.
Default: ``auto``
See **notmuch-search-terms(7)** for details of the supported syntax for
<search-terms>.

View file

@ -110,22 +110,27 @@ Supported options for **show** include
supported with --format=json and --format=sexp), and the
multipart/signed part will be replaced by the signed data.
``--decrypt``
Decrypt any MIME encrypted parts found in the selected content
(ie. "multipart/encrypted" parts). Status of the decryption will
be reported (currently only supported with --format=json and
--format=sexp) and on successful decryption the
multipart/encrypted part will be replaced by the decrypted
content.
``--decrypt=(false|auto|true)``
If ``true``, decrypt any MIME encrypted parts found in the
selected content (i.e. "multipart/encrypted" parts). Status of
the decryption will be reported (currently only supported
with --format=json and --format=sexp) and on successful
decryption the multipart/encrypted part will be replaced by
the decrypted content.
If a session key is already known for the message, then it will be
decrypted automatically unless the user explicitly sets
``--decrypt=false``.
If ``auto``, and a session key is already known for the
message, then it will be decrypted, but notmuch will not try
to access the user's keys.
Decryption expects a functioning **gpg-agent(1)** to provide any
needed credentials. Without one, the decryption will fail.
Use ``false`` to avoid even automatic decryption.
Implies --verify.
Non-automatic decryption expects a functioning
**gpg-agent(1)** to provide any needed credentials. Without
one, the decryption will fail.
Note: ``true`` implies --verify.
Default: ``auto``
``--exclude=(true|false)``
Specify whether to omit threads only matching search.tag\_exclude

View file

@ -593,7 +593,7 @@ the given type."
(set-buffer-multibyte nil))
(let ((args `("show" "--format=raw"
,(format "--part=%s" (plist-get part :id))
,@(when process-crypto '("--decrypt"))
,@(when process-crypto '("--decrypt=true"))
,(notmuch-id-to-query (plist-get msg :id))))
(coding-system-for-read
(if binaryp 'no-conversion

View file

@ -181,7 +181,7 @@ mutiple parts get a header."
reply
original)
(when process-crypto
(setq args (append args '("--decrypt"))))
(setq args (append args '("--decrypt=true"))))
(if reply-all
(setq args (append args '("--reply-to=all")))

View file

@ -32,7 +32,7 @@ is a possibly empty forest of replies.
"
(let ((args '("show" "--format=sexp" "--format-version=4")))
(if notmuch-show-process-crypto
(setq args (append args '("--decrypt"))))
(setq args (append args '("--decrypt=true"))))
(setq args (append args search-terms))
(apply #'notmuch-call-notmuch-sexp args)))

View file

@ -704,8 +704,6 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])
};
int format = FORMAT_DEFAULT;
int reply_all = true;
bool decrypt = false;
bool decrypt_set = false;
notmuch_opt_desc_t options[] = {
{ .opt_keyword = &format, .name = "format", .keywords =
@ -719,7 +717,12 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])
(notmuch_keyword_t []){ { "all", true },
{ "sender", false },
{ 0, 0 } } },
{ .opt_bool = &decrypt, .name = "decrypt", .present = &decrypt_set },
{ .opt_keyword = (int*)(&params.crypto.decrypt), .name = "decrypt",
.keyword_no_arg_value = "true", .keywords =
(notmuch_keyword_t []){ { "false", NOTMUCH_DECRYPT_FALSE },
{ "auto", NOTMUCH_DECRYPT_AUTO },
{ "true", NOTMUCH_DECRYPT_NOSTASH },
{ 0, 0 } } },
{ .opt_inherit = notmuch_shared_options },
{ }
};
@ -729,8 +732,6 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[])
return EXIT_FAILURE;
notmuch_process_shared_options (argv[0]);
if (decrypt_set)
params.crypto.decrypt = decrypt ? NOTMUCH_DECRYPT_NOSTASH : NOTMUCH_DECRYPT_FALSE;
notmuch_exit_if_unsupported_format ();

View file

@ -1085,8 +1085,6 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
bool exclude = true;
bool entire_thread_set = false;
bool single_message;
bool decrypt = false;
bool decrypt_set = false;
notmuch_opt_desc_t options[] = {
{ .opt_keyword = &format, .name = "format", .keywords =
@ -1101,7 +1099,12 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
{ .opt_bool = &params.entire_thread, .name = "entire-thread",
.present = &entire_thread_set },
{ .opt_int = &params.part, .name = "part" },
{ .opt_bool = &decrypt, .name = "decrypt", .present = &decrypt_set },
{ .opt_keyword = (int*)(&params.crypto.decrypt), .name = "decrypt",
.keyword_no_arg_value = "true", .keywords =
(notmuch_keyword_t []){ { "false", NOTMUCH_DECRYPT_FALSE },
{ "auto", NOTMUCH_DECRYPT_AUTO },
{ "true", NOTMUCH_DECRYPT_NOSTASH },
{ 0, 0 } } },
{ .opt_bool = &params.crypto.verify, .name = "verify" },
{ .opt_bool = &params.output_body, .name = "body" },
{ .opt_bool = &params.include_html, .name = "include-html" },
@ -1115,16 +1118,9 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
notmuch_process_shared_options (argv[0]);
if (decrypt_set) {
if (decrypt) {
/* we do not need or want to ask for session keys */
params.crypto.decrypt = NOTMUCH_DECRYPT_NOSTASH;
/* decryption implies verification */
params.crypto.verify = true;
} else {
params.crypto.decrypt = NOTMUCH_DECRYPT_FALSE;
}
}
/* explicit decryption implies verification */
if (params.crypto.decrypt == NOTMUCH_DECRYPT_NOSTASH)
params.crypto.verify = true;
/* specifying a part implies single message display */
single_message = params.part >= 0;

View file

@ -223,7 +223,7 @@ output=$(notmuch search mimetype:multipart/encrypted and mimetype:application/pg
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; test encrypted message 001 (encrypted inbox)"
test_begin_subtest "decryption, --format=text"
output=$(notmuch show --format=text --decrypt subject:"test encrypted message 001" \
output=$(notmuch show --format=text --decrypt=true subject:"test encrypted message 001" \
| notmuch_show_sanitize_all \
| sed -e 's|"created": [1234567890]*|"created": 946728000|')
expected=' message{ id:XXXXX depth:0 match:1 excluded:0 filename:XXXXX
@ -255,7 +255,7 @@ test_expect_equal \
"$expected"
test_begin_subtest "decryption, --format=json"
output=$(notmuch show --format=json --decrypt subject:"test encrypted message 001" \
output=$(notmuch show --format=json --decrypt=true subject:"test encrypted message 001" \
| notmuch_json_show_sanitize \
| sed -e 's|"created": [1234567890]*|"created": 946728000|')
expected='[[[{"id": "XXXXX",
@ -293,7 +293,7 @@ test_expect_equal_json \
"$expected"
test_begin_subtest "decryption, --format=json, --part=4"
output=$(notmuch show --format=json --part=4 --decrypt subject:"test encrypted message 001" \
output=$(notmuch show --format=json --part=4 --decrypt=true subject:"test encrypted message 001" \
| notmuch_json_show_sanitize \
| sed -e 's|"created": [1234567890]*|"created": 946728000|')
expected='{"id": 4,
@ -307,13 +307,13 @@ test_begin_subtest "decrypt attachment (--part=5 --format=raw)"
notmuch show \
--format=raw \
--part=5 \
--decrypt \
--decrypt=true \
subject:"test encrypted message 001" >OUTPUT
test_expect_equal_file TESTATTACHMENT OUTPUT
test_begin_subtest "decryption failure with missing key"
mv "${GNUPGHOME}"{,.bak}
output=$(notmuch show --format=json --decrypt subject:"test encrypted message 001" \
output=$(notmuch show --format=json --decrypt=true subject:"test encrypted message 001" \
| notmuch_json_show_sanitize \
| sed -e 's|"created": [1234567890]*|"created": 946728000|')
expected='[[[{"id": "XXXXX",
@ -351,7 +351,7 @@ test_expect_success \
test_begin_subtest "decryption + signature verification"
test_subtest_broken_gmime_2
output=$(notmuch show --format=json --decrypt subject:"test encrypted message 002" \
output=$(notmuch show --format=json --decrypt=true subject:"test encrypted message 002" \
| notmuch_json_show_sanitize \
| sed -e 's|"created": [1234567890]*|"created": 946728000|')
expected='[[[{"id": "XXXXX",
@ -384,7 +384,7 @@ test_expect_equal_json \
"$expected"
test_begin_subtest "reply to encrypted message"
output=$(notmuch reply --decrypt subject:"test encrypted message 002" \
output=$(notmuch reply --decrypt=true subject:"test encrypted message 002" \
| notmuch_drop_mail_headers In-Reply-To References)
expected='From: Notmuch Test Suite <test_suite@notmuchmail.org>
Subject: Re: test encrypted message 002

View file

@ -197,14 +197,14 @@ test_expect_equal \
"$output" \
"$expected"
test_begin_subtest "show one of the messages with --decrypt"
output=$(notmuch show --decrypt thread:0000000000000001 | awk '/^\014part}/{ f=0 }; { if (f) { print $0 } } /^\014part{ ID: 3/{ f=1 }')
test_begin_subtest "show one of the messages with --decrypt=true"
output=$(notmuch show --decrypt=true thread:0000000000000001 | awk '/^\014part}/{ f=0 }; { if (f) { print $0 } } /^\014part{ ID: 3/{ f=1 }')
expected='This is a test encrypted message with a wumpus.'
test_expect_equal \
"$output" \
"$expected"
test_begin_subtest "Ensure that we cannot show the message without --decrypt"
test_begin_subtest "Ensure that we cannot show the message with --decrypt=auto"
output=$(notmuch show thread:0000000000000001 | awk '/^\014part}/{ f=0 }; { if (f) { print $0 } } /^\014part{ ID: 3/{ f=1 }')
expected='Non-text part: application/octet-stream'
test_expect_equal \

View file

@ -65,4 +65,36 @@ flags 1
EOF
test_expect_equal_file EXPECTED OUTPUT
test_begin_subtest "test keyword arguments without value"
$TEST_DIRECTORY/arg-test --boolkeyword bananas > OUTPUT
cat <<EOF > EXPECTED
boolkeyword 1
positional arg 1 bananas
EOF
test_expect_equal_file EXPECTED OUTPUT
test_begin_subtest "test keyword arguments with non-default value separted by a space"
$TEST_DIRECTORY/arg-test --boolkeyword false bananas > OUTPUT
cat <<EOF > EXPECTED
boolkeyword 0
positional arg 1 bananas
EOF
test_expect_equal_file EXPECTED OUTPUT
test_begin_subtest "test keyword arguments without value at the end"
$TEST_DIRECTORY/arg-test bananas --boolkeyword > OUTPUT
cat <<EOF > EXPECTED
boolkeyword 1
positional arg 1 bananas
EOF
test_expect_equal_file EXPECTED OUTPUT
test_begin_subtest "test keyword arguments without value but with = (should be an error)"
$TEST_DIRECTORY/arg-test bananas --boolkeyword= > OUTPUT 2>&1
cat <<EOF > EXPECTED
Unknown keyword argument "" for option "boolkeyword".
Unrecognized option: --boolkeyword=
EOF
test_expect_equal_file EXPECTED OUTPUT
test_done

View file

@ -191,7 +191,7 @@ This is an error (see *Notmuch errors* for more details)
=== ERROR ===
[XXX]
This is an error
command: YYY/notmuch_fail show --format\\=sexp --format-version\\=4 --decrypt --exclude\\=false \\' \\* \\'
command: YYY/notmuch_fail show --format\\=sexp --format-version\\=4 --decrypt\\=true --exclude\\=false \\' \\* \\'
exit status: 1
stderr:
This is an error

View file

@ -7,13 +7,14 @@ int main(int argc, char **argv){
int opt_index=1;
int kw_val=0;
int kwb_val=0;
int fl_val=0;
int int_val=0;
const char *pos_arg1=NULL;
const char *pos_arg2=NULL;
const char *string_val=NULL;
bool bool_val = false;
bool fl_set = false, int_set = false, bool_set = false,
bool fl_set = false, int_set = false, bool_set = false, kwb_set = false,
kw_set = false, string_set = false, pos1_set = false, pos2_set = false;
notmuch_opt_desc_t parent_options[] = {
@ -33,6 +34,12 @@ int main(int argc, char **argv){
{ "one", 1 },
{ "two", 2 },
{ 0, 0 } } },
{ .opt_keyword = &kwb_val, .name = "boolkeyword", .present = &kwb_set,
.keyword_no_arg_value = "true", .keywords =
(notmuch_keyword_t []){ { "false", 0 },
{ "true", 1 },
{ "auto", 2 },
{ 0, 0 } } },
{ .opt_inherit = parent_options },
{ .opt_string = &string_val, .name = "string", .present = &string_set },
{ .opt_position = &pos_arg1, .present = &pos1_set },
@ -51,6 +58,9 @@ int main(int argc, char **argv){
if (kw_set)
printf("keyword %d\n", kw_val);
if (kwb_set)
printf("boolkeyword %d\n", kwb_val);
if (fl_set)
printf("flags %d\n", fl_val);

View file

@ -1 +1 @@
0.26~rc0
0.26~rc1