From e3e03fb718de9790bc9b7a78210f42559bff24e5 Mon Sep 17 00:00:00 2001 From: Stefano Zacchiroli Date: Thu, 21 Apr 2016 14:10:25 -0400 Subject: [PATCH 001/179] notmuch-mutt: use env to locate perl for increased portability Note: this patch drops -w from the shebang line, but we still have "use warnings" in the script, which is superior anyhow. Thanks Andreas Tolfsen for the suggestion. --- contrib/notmuch-mutt/notmuch-mutt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/notmuch-mutt/notmuch-mutt b/contrib/notmuch-mutt/notmuch-mutt index b47b3651..97fb2d85 100755 --- a/contrib/notmuch-mutt/notmuch-mutt +++ b/contrib/notmuch-mutt/notmuch-mutt @@ -1,4 +1,4 @@ -#!/usr/bin/perl -w +#!/usr/bin/env perl # # notmuch-mutt - notmuch (of a) helper for Mutt # From fdce7eb54532a0bdbd56ca3d56099b190eb7284c Mon Sep 17 00:00:00 2001 From: David Edmondson Date: Sat, 30 Apr 2016 07:51:47 +0100 Subject: [PATCH 002/179] emacs: Observe the charset of MIME parts when reading them. `notmuch--get-bodypart-raw' previously assumed that all non-binary MIME parts could be successfully read by assuming that they were UTF-8 encoded. This was demonstrated to be wrong, specifically when a part was marked as ISO8859-1 and included accented characters (which were incorrectly rendered as a result). Rather than assuming UTF-8, attempt to use the part's declared charset when reading it, falling back to US-ASCII if the declared charset is unknown, unsupported or invalid. --- emacs/notmuch-lib.el | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index 78978ee3..f05ded6f 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -23,6 +23,7 @@ ;;; Code: +(require 'mm-util) (require 'mm-view) (require 'mm-decode) (require 'cl) @@ -572,7 +573,20 @@ the given type." ,@(when process-crypto '("--decrypt")) ,(notmuch-id-to-query (plist-get msg :id)))) (coding-system-for-read - (if binaryp 'no-conversion 'utf-8))) + (if binaryp 'no-conversion + (let ((coding-system (mm-charset-to-coding-system + (plist-get part :content-charset)))) + ;; Sadly, + ;; `mm-charset-to-coding-system' seems + ;; to return things that are not + ;; considered acceptable values for + ;; `coding-system-for-read'. + (if (coding-system-p coding-system) + coding-system + ;; RFC 2047 says that the default + ;; charset is US-ASCII. RFC6657 + ;; complicates this somewhat. + 'us-ascii))))) (apply #'call-process notmuch-command nil '(t nil) nil args) (buffer-string)))))) (when (and cache data) From 64b0d21da00a2f5fb7a5b0452b11b50607932fc7 Mon Sep 17 00:00:00 2001 From: David Edmondson Date: Sat, 30 Apr 2016 07:51:48 +0100 Subject: [PATCH 003/179] emacs: Tell `message-mode' that outgoing messages are email. When composing messages (including replies, etc.), indicate to `message-mode' definitively that the message is email (as opposed to Usenet news) rather than having it attempt to determine this for itself. This causes `message-mode' to observe such variables as `message-default-mail-headers', which previously happened haphazardly. --- emacs/notmuch-mua.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index 04459750..399e1380 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -338,7 +338,10 @@ modified. This function is notmuch addaptation of ;; We need to convert any string input, eg from rmail-start-mail. (dolist (h other-headers other-headers) (if (stringp (car h)) (setcar h (intern (capitalize (car h)))))))) - (args (list yank-action send-actions))) + (args (list yank-action send-actions)) + ;; Cause `message-setup-1' to do things relevant for mail, + ;; such as observe `message-default-mail-headers'. + (message-this-is-mail t)) ;; message-setup-1 in Emacs 23 does not accept return-action ;; argument. Pass it only if it is supplied by the caller. This ;; will never be the case when we're called by `compose-mail' in From 2b7b32fc476d0d9b52fc69ee1690b15ea1360207 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 13 Apr 2016 21:32:46 +0300 Subject: [PATCH 004/179] configure: SC2006: Use $(..) instead of legacy `..` Fix shellcheck warnings. --- configure | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 4fc31ccf..0c6cdb01 100755 --- a/configure +++ b/configure @@ -250,7 +250,7 @@ if [ -z "$LIBDIR" ] ; then libdir_expanded="${PREFIX}/lib" else # very non-general variable expansion - libdir_expanded=`echo "$LIBDIR" | sed "s|\\${prefix}|${PREFIX}|g; s|\\$prefix/|${PREFIX}/|; s|//*|/|g"` + libdir_expanded=$(echo "$LIBDIR" | sed "s|\\${prefix}|${PREFIX}|g; s|\\$prefix/|${PREFIX}/|; s|//*|/|g") fi cat < Date: Wed, 13 Apr 2016 21:32:47 +0300 Subject: [PATCH 005/179] configure: SC2059: Don't use variables in the printf format string. Fix shellcheck warnings. Use printf "..%s.." "$foo". --- configure | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/configure b/configure index 0c6cdb01..d4f56b90 100755 --- a/configure +++ b/configure @@ -387,7 +387,7 @@ EOF else default_xapian_backend=chert fi - printf "${default_xapian_backend}\n"; + printf "%s\n" "${default_xapian_backend}"; rm -rf test.db _default_backend _default_backend.cc fi # we need to have a version >= 2.6.5 to avoid a crypto bug. We need @@ -461,7 +461,7 @@ for name in ${PYTHON} python python2 python3; do if command -v $name > /dev/null; then have_python=1 python=$name - printf "Yes ($name).\n" + printf "Yes (%s).\n" "$name" break fi done @@ -560,11 +560,11 @@ elif [ $uname = "OpenBSD" ] ; then platform=OPENBSD linker_resolves_library_dependencies=0 elif [ $uname = "Linux" ] || [ $uname = "GNU" ] ; then - printf "$uname\n" + printf "%s\n" "$uname" platform="$uname" linker_resolves_library_dependencies=1 - printf "Checking for $libdir_expanded in ldconfig... " + printf "Checking for %s in ldconfig... " "$libdir_expanded" ldconfig_paths=$(/sbin/ldconfig -N -X -v 2>/dev/null | sed -n -e 's,^\(/.*\):\( (.*)\)\?$,\1,p') # Separate ldconfig_paths only on newline (not on any potential # embedded space characters in any filenames). Note, we use a @@ -815,7 +815,7 @@ for flag in -Wall -Wextra -Wwrite-strings; do WARN_CXXFLAGS="${WARN_CXXFLAGS}${WARN_CXXFLAGS:+ }${flag}" fi done -printf "\n\t${WARN_CXXFLAGS}\n" +printf "\n\t%s\n" "${WARN_CXXFLAGS}" WARN_CFLAGS="${WARN_CXXFLAGS}" printf "Checking for available C compiler warning flags... " @@ -825,7 +825,7 @@ for flag in -Wmissing-declarations; do WARN_CFLAGS="${WARN_CFLAGS}${WARN_CFLAGS:+ }${flag}" fi done -printf "\n\t${WARN_CFLAGS}\n" +printf "\n\t%s\n" "${WARN_CFLAGS}" rm -f minimal minimal.c _libversion.c _libversion _libversion.sh From b191de511e6148112fa2432779f6dc6c7cb501f9 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 13 Apr 2016 21:32:48 +0300 Subject: [PATCH 006/179] configure: SC2034: glib_cflags and glib_ldflags appear unused. Fix shellcheck warnings. --- configure | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/configure b/configure index d4f56b90..f3498e3e 100755 --- a/configure +++ b/configure @@ -413,8 +413,9 @@ have_glib=0 if pkg-config --exists 'glib-2.0 >= 2.22'; then printf "Yes.\n" have_glib=1 - glib_cflags=$(pkg-config --cflags glib-2.0) - glib_ldflags=$(pkg-config --libs glib-2.0) + # these are included in gmime cflags and ldflags + # glib_cflags=$(pkg-config --cflags glib-2.0) + # glib_ldflags=$(pkg-config --libs glib-2.0) else printf "No.\n" errors=$((errors + 1)) From 2c1a7321e9054e0bd8a7ebf1b4316baa4a65504d Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 13 Apr 2016 21:32:49 +0300 Subject: [PATCH 007/179] configure: SC2016: Expressions don't expand in single quotes Fix shellcheck warnings. Use double quotes but escape $ to make it look more intentional. --- configure | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure b/configure index f3498e3e..6231d2b7 100755 --- a/configure +++ b/configure @@ -491,11 +491,11 @@ else fi if [ -z "${EMACSLISPDIR}" ]; then - EMACSLISPDIR='$(prefix)/share/emacs/site-lisp' + EMACSLISPDIR="\$(prefix)/share/emacs/site-lisp" fi if [ -z "${EMACSETCDIR}" ]; then - EMACSETCDIR='$(prefix)/share/emacs/site-lisp' + EMACSETCDIR="\$(prefix)/share/emacs/site-lisp" fi printf "Checking if emacs is available... " From 73339ade993b7ae14d26808cfd6efcd0da712f3d Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 15 Apr 2016 22:29:15 +0300 Subject: [PATCH 008/179] cli: remove leftover dir variable No functional changes. --- notmuch-new.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/notmuch-new.c b/notmuch-new.c index 04cb5cac..2d975eb5 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -351,7 +351,6 @@ add_files (notmuch_database_t *notmuch, const char *path, add_files_state_t *state) { - DIR *dir = NULL; struct dirent *entry = NULL; char *next = NULL; time_t fs_mtime, db_mtime; @@ -655,8 +654,6 @@ add_files (notmuch_database_t *notmuch, DONE: if (next) talloc_free (next); - if (dir) - closedir (dir); if (fs_entries) { for (i = 0; i < num_fs_entries; i++) free (fs_entries[i]); From 43eb32527bfb9663a09c8132af840d0b66a85cff Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Tue, 3 May 2016 20:23:44 +0300 Subject: [PATCH 009/179] configure: replace ${CXXLAGS} with ${CXXFLAGS_for_sh} Variable CXXLAGS expands to nothing, CXXFLAGS something unusable here; CXXFLAGS_for_sh expands to what we expect here. --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 6231d2b7..49fc7391 100755 --- a/configure +++ b/configure @@ -380,7 +380,7 @@ int main(int argc, char** argv) { Xapian::WritableDatabase db("test.db",Xapian::DB_CREATE_OR_OPEN); } EOF - ${CXX} ${CXXLAGS} ${xapian_cxxflags} _default_backend.cc -o _default_backend ${xapian_ldflags} + ${CXX} ${CXXFLAGS_for_sh} ${xapian_cxxflags} _default_backend.cc -o _default_backend ${xapian_ldflags} ./_default_backend if [ -f test.db/iamglass ]; then default_xapian_backend=glass From 124a67e96ecab5495c0f17b6875d53dfd67ff137 Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Fri, 6 May 2016 21:11:25 +0300 Subject: [PATCH 010/179] configure: add set -u In case of any unset variable, make ./configure exit with nonzero value; an attempt to expand an unset variable is a bug in the script (usually a spelling mistake) and those should not pass through unnoticed. --- configure | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 49fc7391..c24a9c85 100755 --- a/configure +++ b/configure @@ -1,5 +1,7 @@ #! /bin/sh +set -u + # Test whether this shell is capable of parameter substring processing. ( option='a/b'; : ${option#*/} ) 2>/dev/null || { echo " @@ -480,6 +482,7 @@ if pkg-config --exists valgrind; then else printf "No (but that's fine).\n" have_valgrind=0 + valgrind_cflags= fi printf "Checking for bash-completion (>= 1.90)... " @@ -490,11 +493,11 @@ else WITH_BASH=0 fi -if [ -z "${EMACSLISPDIR}" ]; then +if [ -z "${EMACSLISPDIR-}" ]; then EMACSLISPDIR="\$(prefix)/share/emacs/site-lisp" fi -if [ -z "${EMACSETCDIR}" ]; then +if [ -z "${EMACSETCDIR-}" ]; then EMACSETCDIR="\$(prefix)/share/emacs/site-lisp" fi From 1871be319a28d8bba8b368f6b056d2c8448afcf6 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 22 Mar 2016 07:54:43 -0300 Subject: [PATCH 011/179] configure: detect Xapian:FieldProcessor Rather than check versions, it seems more robust to do a test compile. --- configure | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/configure b/configure index c24a9c85..ba12c5a5 100755 --- a/configure +++ b/configure @@ -358,9 +358,10 @@ if [ ${have_xapian} = "0" ]; then errors=$((errors + 1)) fi -# Compaction is only supported on Xapian > 1.2.6 have_xapian_compact=0 +have_xapian_field_processor=0 if [ ${have_xapian} = "1" ]; then + # Compaction is only supported on Xapian > 1.2.6 printf "Checking for Xapian compaction support... " case "${xapian_version}" in 0.*|1.[01].*|1.2.[0-5]) @@ -371,10 +372,23 @@ if [ ${have_xapian} = "1" ]; then *) printf "Unknown version.\n" ;; esac -fi -default_xapian_backend="" -if [ ${have_xapian} = "1" ]; then + printf "Checking for Xapian FieldProcessor API... " + cat>_field_processor.cc< +class TitleFieldProcessor : public Xapian::FieldProcessor { }; +EOF + if ${CXX} ${CXXFLAGS_for_sh} ${xapian_cxxflags} -c _field_processor.cc -o _field_processor.o > /dev/null 2>&1 + then + have_xapian_field_processor=1 + printf "Yes.\n" + else + printf "No. (optional)\n" + fi + + rm -f _field_processor.o _field_processor.cc + + default_xapian_backend="" printf "Testing default Xapian backend... " cat >_default_backend.cc < @@ -392,6 +406,7 @@ EOF printf "%s\n" "${default_xapian_backend}"; rm -rf test.db _default_backend _default_backend.cc fi + # we need to have a version >= 2.6.5 to avoid a crypto bug. We need # 2.6.7 for permissive "From " header handling. GMIME_MINVER=2.6.7 @@ -1001,6 +1016,9 @@ HAVE_D_TYPE = ${have_d_type} # Whether the Xapian version in use supports compaction HAVE_XAPIAN_COMPACT = ${have_xapian_compact} +# Whether the Xapian version in use supports field processors +HAVE_XAPIAN_FIELD_PROCESSOR = ${have_xapian_field_processor} + # Whether the getpwuid_r function is standards-compliant # (if not, then notmuch will #define _POSIX_PTHREAD_SEMANTICS # to enable the standards-compliant version -- needed for Solaris) @@ -1075,6 +1093,7 @@ CONFIGURE_CFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS) \\ -DSTD_GETPWUID=\$(STD_GETPWUID) \\ -DSTD_ASCTIME=\$(STD_ASCTIME) \\ -DHAVE_XAPIAN_COMPACT=\$(HAVE_XAPIAN_COMPACT) \\ + -DHAVE_XAPIAN_FIELD_PROCESSOR=\$(HAVE_XAPIAN_FIELD_PROCESSOR) \\ -DUTIL_BYTE_ORDER=\$(UTIL_BYTE_ORDER) CONFIGURE_CXXFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS) \\ @@ -1089,6 +1108,7 @@ CONFIGURE_CXXFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS) \\ -DSTD_GETPWUID=\$(STD_GETPWUID) \\ -DSTD_ASCTIME=\$(STD_ASCTIME) \\ -DHAVE_XAPIAN_COMPACT=\$(HAVE_XAPIAN_COMPACT) \\ + -DHAVE_XAPIAN_FIELD_PROCESSOR=\$(HAVE_XAPIAN_FIELD_PROCESSOR) \\ -DUTIL_BYTE_ORDER=\$(UTIL_BYTE_ORDER) CONFIGURE_LDFLAGS = \$(GMIME_LDFLAGS) \$(TALLOC_LDFLAGS) \$(ZLIB_LDFLAGS) \$(XAPIAN_LDFLAGS) @@ -1102,6 +1122,9 @@ cat > sh.config < Date: Tue, 22 Mar 2016 07:54:44 -0300 Subject: [PATCH 012/179] lib: optionally support single argument date: queries This relies on the FieldProcessor API, which is only present in xapian >= 1.3. --- doc/man7/notmuch-search-terms.rst | 22 +++++++++++++++++++--- lib/database-private.h | 3 +++ lib/database.cc | 6 ++++++ lib/parse-time-vrp.cc | 21 +++++++++++++++++++++ lib/parse-time-vrp.h | 5 +++++ test/T500-search-date.sh | 6 ++++++ 6 files changed, 60 insertions(+), 3 deletions(-) diff --git a/doc/man7/notmuch-search-terms.rst b/doc/man7/notmuch-search-terms.rst index 2fbc16d1..adedf5a3 100644 --- a/doc/man7/notmuch-search-terms.rst +++ b/doc/man7/notmuch-search-terms.rst @@ -281,9 +281,10 @@ matches from the beginning of January to the end of February. date:..! can be used as a shorthand for date:... The expansion takes place before interpretation, and thus, for example, date:monday..! matches from the beginning of Monday until the end of -Monday. (Note that entering date: without "..", for example -date:yesterday, won't work, as it's not interpreted as a range -expression at all. Again, use date:yesterday..!) +Monday. +With **Xapian Field Processor** support (see below), non-range +date queries such as date:yesterday will work, but otherwise +will give unexpected results; if in doubt use date:yesterday..! Currently, we do not support spaces in range expressions. You can replace the spaces with '\_', or (in most cases) '-', or (in some cases) @@ -370,6 +371,21 @@ Time zones Some time zone codes, e.g. UTC, EET. +XAPIAN FIELD PROCESSORS +======================= + +Certain optional features of the notmuch query processor rely on the +presence of the Xapian field processor API. You can determine if your +notmuch was built against a sufficiently recent version of Xapian by running + +:: + + % notmuch config get built_with.field_processor + +Currently the following features require field processor support: + +- non-range date queries, e.g. "date:today" + SEE ALSO ======== diff --git a/lib/database-private.h b/lib/database-private.h index 3fb10f7a..e1962f43 100644 --- a/lib/database-private.h +++ b/lib/database-private.h @@ -176,6 +176,9 @@ struct _notmuch_database { Xapian::TermGenerator *term_gen; Xapian::ValueRangeProcessor *value_range_processor; Xapian::ValueRangeProcessor *date_range_processor; +#if HAVE_XAPIAN_FIELD_PROCESSOR + Xapian::FieldProcessor *date_field_processor; +#endif Xapian::ValueRangeProcessor *last_mod_range_processor; }; diff --git a/lib/database.cc b/lib/database.cc index c8c5e261..ebe019fb 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -1000,6 +1000,12 @@ notmuch_database_open_verbose (const char *path, notmuch->term_gen->set_stemmer (Xapian::Stem ("english")); notmuch->value_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_TIMESTAMP); notmuch->date_range_processor = new ParseTimeValueRangeProcessor (NOTMUCH_VALUE_TIMESTAMP); +#if HAVE_XAPIAN_FIELD_PROCESSOR + /* This currently relies on the query parser to pass anything + * with a .. to the range processor */ + notmuch->date_field_processor = new DateFieldProcessor(); + notmuch->query_parser->add_boolean_prefix("date", notmuch->date_field_processor); +#endif notmuch->last_mod_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_LAST_MOD, "lastmod:"); notmuch->query_parser->set_default_op (Xapian::Query::OP_AND); diff --git a/lib/parse-time-vrp.cc b/lib/parse-time-vrp.cc index 03804cf5..b15b77c6 100644 --- a/lib/parse-time-vrp.cc +++ b/lib/parse-time-vrp.cc @@ -64,3 +64,24 @@ ParseTimeValueRangeProcessor::operator() (std::string &begin, std::string &end) return valno; } + +#if HAVE_XAPIAN_FIELD_PROCESSOR +/* XXX TODO: is throwing an exception the right thing to do here? */ +Xapian::Query DateFieldProcessor::operator()(const std::string & str) { + time_t from, to, now; + + /* Use the same 'now' for begin and end. */ + if (time (&now) == (time_t) -1) + throw Xapian::QueryParserError("Unable to get current time"); + + if (parse_time_string (str.c_str (), &from, &now, PARSE_TIME_ROUND_DOWN)) + throw Xapian::QueryParserError ("Didn't understand date specification '" + str + "'"); + + if (parse_time_string (str.c_str (), &to, &now, PARSE_TIME_ROUND_UP_INCLUSIVE)) + throw Xapian::QueryParserError ("Didn't understand date specification '" + str + "'"); + + return Xapian::Query(Xapian::Query::OP_AND, + Xapian::Query(Xapian::Query::OP_VALUE_GE, 0, Xapian::sortable_serialise ((double) from)), + Xapian::Query(Xapian::Query::OP_VALUE_LE, 0, Xapian::sortable_serialise ((double) to))); +} +#endif diff --git a/lib/parse-time-vrp.h b/lib/parse-time-vrp.h index 094c4f87..3bd12bf3 100644 --- a/lib/parse-time-vrp.h +++ b/lib/parse-time-vrp.h @@ -37,4 +37,9 @@ public: Xapian::valueno operator() (std::string &begin, std::string &end); }; +#if HAVE_XAPIAN_FIELD_PROCESSOR +class DateFieldProcessor : public Xapian::FieldProcessor { + Xapian::Query operator()(const std::string & str); +}; +#endif #endif /* NOTMUCH_PARSE_TIME_VRP_H */ diff --git a/test/T500-search-date.sh b/test/T500-search-date.sh index f5cea421..198a2e60 100755 --- a/test/T500-search-date.sh +++ b/test/T500-search-date.sh @@ -12,6 +12,12 @@ test_begin_subtest "Absolute date range with 'same' operator" output=$(notmuch search date:2010-12-16..! | notmuch_search_sanitize) test_expect_equal "$output" "thread:XXX 2010-12-16 [1/1] Olivier Berger; Essai accentué (inbox unread)" +if [ "${NOTMUCH_HAVE_XAPIAN_FIELD_PROCESSOR}" = "1" ]; then + test_begin_subtest "Absolute date field" + output=$(notmuch search date:2010-12-16 | notmuch_search_sanitize) + test_expect_equal "$output" "thread:XXX 2010-12-16 [1/1] Olivier Berger; Essai accentué (inbox unread)" +fi + test_begin_subtest "Absolute time range with TZ" notmuch search date:18-Nov-2009_02:19:26-0800..2009-11-18_04:49:52-06:00 | notmuch_search_sanitize > OUTPUT cat <EXPECTED From 792bea5aff135107fd970f51f3ef8c65f68194de Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 22 Mar 2016 07:54:45 -0300 Subject: [PATCH 013/179] lib/cli: add library API / CLI for compile time options This is intentionally low tech; if we have more than two options it may make sense to build up what infrastructure is provided. --- doc/man1/notmuch-config.rst | 5 +++++ lib/Makefile.local | 1 + lib/built-with.c | 34 ++++++++++++++++++++++++++++++++++ lib/notmuch.h | 5 +++++ notmuch-config.c | 23 +++++++++++++++++++++++ test/T030-config.sh | 6 ++++-- test/T040-setup.sh | 6 ++++-- test/test-lib.sh | 6 ++++++ 8 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 lib/built-with.c diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst index 40c12721..26a8eb14 100644 --- a/doc/man1/notmuch-config.rst +++ b/doc/man1/notmuch-config.rst @@ -132,6 +132,11 @@ The available configuration items are described below. Default: ``gpg``. + **built_with.** + + Compile time feature . Current possibilities include + "compact" (see **notmuch-compact(1)**) + and "field_processor" (see **notmuch-search-terms(7)**). ENVIRONMENT =========== diff --git a/lib/Makefile.local b/lib/Makefile.local index 3a070907..36c39243 100644 --- a/lib/Makefile.local +++ b/lib/Makefile.local @@ -39,6 +39,7 @@ libnotmuch_c_srcs = \ $(dir)/message-file.c \ $(dir)/messages.c \ $(dir)/sha1.c \ + $(dir)/built-with.c \ $(dir)/tags.c libnotmuch_cxx_srcs = \ diff --git a/lib/built-with.c b/lib/built-with.c new file mode 100644 index 00000000..7ea1d7fd --- /dev/null +++ b/lib/built-with.c @@ -0,0 +1,34 @@ +/* notmuch - Not much of an email program, (just index and search) + * + * Copyright © 2016 David Bremner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/ . + * + * Author: David Bremner + */ + +#include "notmuch.h" +#include "notmuch-private.h" + +notmuch_bool_t +notmuch_built_with (const char *name) +{ + if (STRNCMP_LITERAL (name, "compact") == 0) { + return HAVE_XAPIAN_COMPACT; + } else if (STRNCMP_LITERAL (name, "field_processor") == 0) { + return HAVE_XAPIAN_FIELD_PROCESSOR; + } else { + return FALSE; + } +} diff --git a/lib/notmuch.h b/lib/notmuch.h index cb46fc05..3a092efc 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -1838,6 +1838,11 @@ notmuch_filenames_move_to_next (notmuch_filenames_t *filenames); void notmuch_filenames_destroy (notmuch_filenames_t *filenames); +/** + * interrogate the library for compile time features + */ +notmuch_bool_t +notmuch_built_with (const char *name); /* @} */ NOTMUCH_END_DECLS diff --git a/notmuch-config.c b/notmuch-config.c index d252bb25..01bb1859 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -750,6 +750,8 @@ _item_split (char *item, char **group, char **key) return 0; } +#define BUILT_WITH_PREFIX "built_with." + static int notmuch_config_command_get (notmuch_config_t *config, char *item) { @@ -773,6 +775,9 @@ notmuch_config_command_get (notmuch_config_t *config, char *item) tags = notmuch_config_get_new_tags (config, &length); for (i = 0; i < length; i++) printf ("%s\n", tags[i]); + } else if (STRNCMP_LITERAL (item, BUILT_WITH_PREFIX) == 0) { + printf ("%s\n", + notmuch_built_with (item + strlen (BUILT_WITH_PREFIX)) ? "true" : "false"); } else { char **value; size_t i, length; @@ -804,6 +809,11 @@ notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char { char *group, *key; + if (STRNCMP_LITERAL (item, BUILT_WITH_PREFIX) == 0) { + fprintf (stderr, "Error: read only option: %s\n", item); + return 1; + } + if (_item_split (item, &group, &key)) return 1; @@ -830,6 +840,18 @@ notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char return notmuch_config_save (config); } +static +void +_notmuch_config_list_built_with () +{ + printf("%scompact=%s\n", + BUILT_WITH_PREFIX, + notmuch_built_with ("compact") ? "true" : "false"); + printf("%sfield_processor=%s\n", + BUILT_WITH_PREFIX, + notmuch_built_with ("field_processor") ? "true" : "false"); +} + static int notmuch_config_command_list (notmuch_config_t *config) { @@ -865,6 +887,7 @@ notmuch_config_command_list (notmuch_config_t *config) g_strfreev (groups); + _notmuch_config_list_built_with (); return 0; } diff --git a/test/T030-config.sh b/test/T030-config.sh index f404908a..437269ff 100755 --- a/test/T030-config.sh +++ b/test/T030-config.sh @@ -44,7 +44,7 @@ test_expect_equal "$(notmuch config get foo.nonexistent)" "" test_begin_subtest "List all items" notmuch config set database.path "/canonical/path" -output=$(notmuch config list) +output=$(notmuch config list | notmuch_built_with_sanitize) test_expect_equal "$output" "\ database.path=/canonical/path user.name=Notmuch Test Suite @@ -56,7 +56,9 @@ search.exclude_tags= maildir.synchronize_flags=true crypto.gpg_path=gpg foo.string=this is another string value -foo.list=this;is another;list value;" +foo.list=this;is another;list value; +built_with.compact=something +built_with.field_processor=something" test_begin_subtest "Top level --config=FILE option" cp "${NOTMUCH_CONFIG}" alt-config diff --git a/test/T040-setup.sh b/test/T040-setup.sh index cf0c00bc..be2f0dbd 100755 --- a/test/T040-setup.sh +++ b/test/T040-setup.sh @@ -19,7 +19,7 @@ another.suite@example.com foo bar baz EOF -output=$(notmuch --config=new-notmuch-config config list) +output=$(notmuch --config=new-notmuch-config config list | notmuch_built_with_sanitize) test_expect_equal "$output" "\ database.path=/path/to/maildir user.name=Test Suite @@ -29,6 +29,8 @@ new.tags=foo;bar; new.ignore= search.exclude_tags=baz; maildir.synchronize_flags=true -crypto.gpg_path=gpg" +crypto.gpg_path=gpg +built_with.compact=something +built_with.field_processor=something" test_done diff --git a/test/test-lib.sh b/test/test-lib.sh index ac04b15a..09f87319 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -733,6 +733,12 @@ notmuch_uuid_sanitize () { sed 's/[0-9a-f]\{8\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{12\}/UUID/g' } + +notmuch_built_with_sanitize () +{ + sed 's/^built_with[.]\(.*\)=.*$/built_with.\1=something/' +} + # End of notmuch helper functions # Use test_set_prereq to tell that a particular prerequisite is available. From 1aa6f90a10794951365626830e8ebf29cf3736c8 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 22 Mar 2016 07:54:46 -0300 Subject: [PATCH 014/179] configure: check directly for xapian compaction API This is consistent with the check for FieldProcessor, and probably a bit more robust. --- configure | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/configure b/configure index ba12c5a5..ab42f947 100755 --- a/configure +++ b/configure @@ -361,17 +361,20 @@ fi have_xapian_compact=0 have_xapian_field_processor=0 if [ ${have_xapian} = "1" ]; then - # Compaction is only supported on Xapian > 1.2.6 printf "Checking for Xapian compaction support... " - case "${xapian_version}" in - 0.*|1.[01].*|1.2.[0-5]) - printf "No (only available with Xapian > 1.2.6).\n" ;; - [1-9]*.[0-9]*.[0-9]*) - have_xapian_compact=1 - printf "Yes.\n" ;; - *) - printf "Unknown version.\n" ;; - esac + cat>_compact.cc< +class TestCompactor : public Xapian::Compactor { }; +EOF + if ${CXX} ${CXXFLAGS_for_sh} ${xapian_cxxflags} -c _compact.cc -o _compact.o > /dev/null 2>&1 + then + have_xapian_compact=1 + printf "Yes.\n" + else + printf "No.\n" + fi + + rm -f _compact.o _compact.cc printf "Checking for Xapian FieldProcessor API... " cat>_field_processor.cc< Date: Sat, 14 May 2016 00:29:45 +0300 Subject: [PATCH 015/179] configure: combine common parts of CONFIGURE_C{,XX}FLAGS By combining the common parts of CONFIGURE_CFLAGS and CONFIGURE_CXXFLAGS to a separate make variable and using that as part of their definitions makes setting of these easier, DRYer and less error prone (especially as we cannot check potential typing errors there). --- configure | 45 +++++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/configure b/configure index ab42f947..ce1d6983 100755 --- a/configure +++ b/configure @@ -1084,35 +1084,24 @@ WITH_BASH = ${WITH_BASH} WITH_ZSH = ${WITH_ZSH} # Combined flags for compiling and linking against all of the above -CONFIGURE_CFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS) \\ - -DHAVE_CANONICALIZE_FILE_NAME=\$(HAVE_CANONICALIZE_FILE_NAME) \\ - \$(ZLIB_CFLAGS) \\ - \$(TALLOC_CFLAGS) -DHAVE_VALGRIND=\$(HAVE_VALGRIND) \\ - \$(VALGRIND_CFLAGS) \\ - -DHAVE_STRCASESTR=\$(HAVE_STRCASESTR) \\ - -DHAVE_STRSEP=\$(HAVE_STRSEP) \\ - -DHAVE_TIMEGM=\$(HAVE_TIMEGM) \\ - -DHAVE_D_TYPE=\$(HAVE_D_TYPE) \\ - -DSTD_GETPWUID=\$(STD_GETPWUID) \\ - -DSTD_ASCTIME=\$(STD_ASCTIME) \\ - -DHAVE_XAPIAN_COMPACT=\$(HAVE_XAPIAN_COMPACT) \\ - -DHAVE_XAPIAN_FIELD_PROCESSOR=\$(HAVE_XAPIAN_FIELD_PROCESSOR) \\ - -DUTIL_BYTE_ORDER=\$(UTIL_BYTE_ORDER) +COMMON_CONFIGURE_CFLAGS = \\ + \$(GMIME_CFLAGS) \$(TALLOC_CFLAGS) \$(ZLIB_CFLAGS) \\ + -DHAVE_VALGRIND=\$(HAVE_VALGRIND) \$(VALGRIND_CFLAGS) \\ + -DHAVE_GETLINE=\$(HAVE_GETLINE) \\ + -DHAVE_CANONICALIZE_FILE_NAME=\$(HAVE_CANONICALIZE_FILE_NAME) \\ + -DHAVE_STRCASESTR=\$(HAVE_STRCASESTR) \\ + -DHAVE_STRSEP=\$(HAVE_STRSEP) \\ + -DHAVE_TIMEGM=\$(HAVE_TIMEGM) \\ + -DHAVE_D_TYPE=\$(HAVE_D_TYPE) \\ + -DSTD_GETPWUID=\$(STD_GETPWUID) \\ + -DSTD_ASCTIME=\$(STD_ASCTIME) \\ + -DHAVE_XAPIAN_COMPACT=\$(HAVE_XAPIAN_COMPACT) \\ + -DHAVE_XAPIAN_FIELD_PROCESSOR=\$(HAVE_XAPIAN_FIELD_PROCESSOR) \\ + -DUTIL_BYTE_ORDER=\$(UTIL_BYTE_ORDER) -CONFIGURE_CXXFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS) \\ - -DHAVE_CANONICALIZE_FILE_NAME=\$(HAVE_CANONICALIZE_FILE_NAME) \\ - \$(ZLIB_CFLAGS) \\ - \$(TALLOC_CFLAGS) -DHAVE_VALGRIND=\$(HAVE_VALGRIND) \\ - \$(VALGRIND_CFLAGS) \$(XAPIAN_CXXFLAGS) \\ - -DHAVE_STRCASESTR=\$(HAVE_STRCASESTR) \\ - -DHAVE_STRSEP=\$(HAVE_STRSEP) \\ - -DHAVE_TIMEGM=\$(HAVE_TIMEGM) \\ - -DHAVE_D_TYPE=\$(HAVE_D_TYPE) \\ - -DSTD_GETPWUID=\$(STD_GETPWUID) \\ - -DSTD_ASCTIME=\$(STD_ASCTIME) \\ - -DHAVE_XAPIAN_COMPACT=\$(HAVE_XAPIAN_COMPACT) \\ - -DHAVE_XAPIAN_FIELD_PROCESSOR=\$(HAVE_XAPIAN_FIELD_PROCESSOR) \\ - -DUTIL_BYTE_ORDER=\$(UTIL_BYTE_ORDER) +CONFIGURE_CFLAGS = \$(COMMON_CONFIGURE_CFLAGS) + +CONFIGURE_CXXFLAGS = \$(COMMON_CONFIGURE_CFLAGS) \$(XAPIAN_CXXFLAGS) CONFIGURE_LDFLAGS = \$(GMIME_LDFLAGS) \$(TALLOC_LDFLAGS) \$(ZLIB_LDFLAGS) \$(XAPIAN_LDFLAGS) EOF From eac29763896e457a738f811b2c2ba8f877498e62 Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Sat, 7 May 2016 22:24:18 +0300 Subject: [PATCH 016/179] cli: tell how many messages were precisely matched when expected 1 match In case of notmuch reply and notmuch show --part=N it is required that search terms match to one message. If match count was != 1, error message "Error: search term did not match precisely one message" was too vague to explain what happened. By appending (matched messages) to the error message it makes the problem more understandable (e.g when is '0' user reckons the query had a typo in it). --- notmuch-reply.c | 2 +- notmuch-show.c | 2 +- test/T210-raw.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index 3c6d685c..a74194a3 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -664,7 +664,7 @@ notmuch_reply_format_sprinter(void *ctx, return 1; if (count != 1) { - fprintf (stderr, "Error: search term did not match precisely one message.\n"); + fprintf (stderr, "Error: search term did not match precisely one message (matched %d messages).\n", count); return 1; } diff --git a/notmuch-show.c b/notmuch-show.c index 87e52bbc..3d5033d6 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -904,7 +904,7 @@ do_show_single (void *ctx, return 1; if (count != 1) { - fprintf (stderr, "Error: search term did not match precisely one message.\n"); + fprintf (stderr, "Error: search term did not match precisely one message (matched %d messages).\n", count); return 1; } diff --git a/test/T210-raw.sh b/test/T210-raw.sh index dfea2d19..832a4ad3 100755 --- a/test/T210-raw.sh +++ b/test/T210-raw.sh @@ -8,7 +8,7 @@ add_message test_begin_subtest "Attempt to show multiple raw messages" output=$(notmuch show --format=raw "*" 2>&1) -test_expect_equal "$output" "Error: search term did not match precisely one message." +test_expect_equal "$output" "Error: search term did not match precisely one message (matched 2 messages)." test_begin_subtest "Show a raw message" output=$(notmuch show --format=raw id:msg-001@notmuch-test-suite | notmuch_date_sanitize) From c13ff402c42143941010cd03bd57658d42f0df7d Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Sat, 7 May 2016 22:03:11 +0300 Subject: [PATCH 017/179] emacs: add function to resend message to new recipients The new function notmuch-show-message-resend re-sends message to new recipients using #'message-resend. Recipients are read from minibuffer as a comma-separated string (with some keyboard support including tab completion). Final confirmation before sending is asked. --- emacs/notmuch-address.el | 19 +++++++++++++++++++ emacs/notmuch-show.el | 8 ++++++++ 2 files changed, 27 insertions(+) diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el index aafbe5fb..8b79d340 100644 --- a/emacs/notmuch-address.el +++ b/emacs/notmuch-address.el @@ -249,6 +249,25 @@ called when harvesting finishes." ;; +(defun notmuch-address-from-minibuffer (prompt) + (if (not notmuch-address-command) + (read-string prompt) + (let ((rmap (copy-keymap minibuffer-local-map)) + (omap minibuffer-local-map)) + ;; Configure TAB to start completion when executing read-string. + ;; "Original" minibuffer keymap is restored just before calling + ;; notmuch-address-expand-name as it may also use minibuffer-local-map + ;; (completing-read probably does not but if something else is used there). + (define-key rmap (kbd "TAB") (lambda () + (interactive) + (let ((enable-recursive-minibuffers t) + (minibuffer-local-map omap)) + (notmuch-address-expand-name)))) + (let ((minibuffer-local-map rmap)) + (read-string prompt))))) + +;; + (provide 'notmuch-address) ;;; notmuch-address.el ends here diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index 5d9b7b45..4b8c66fd 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -1855,6 +1855,14 @@ any effects from previous calls to (error "No open messages to forward.")) (notmuch-mua-new-forward-messages open-messages prompt-for-sender))) +(defun notmuch-show-resend-message (addresses) + "Resend the current message." + (interactive (list (notmuch-address-from-minibuffer "Resend to: "))) + (when (y-or-n-p (concat "Confirm resend to " addresses " ")) + (notmuch-show-view-raw-message) + (message-resend addresses) + (notmuch-bury-or-kill-this-buffer))) + (defun notmuch-show-next-message (&optional pop-at-end) "Show the next message. From fd3503e99ee1977b3d5264755ec4ab2fe1e0078a Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Sat, 7 May 2016 22:03:12 +0300 Subject: [PATCH 018/179] emacs: bind notmuch-show-resend-message to 'b' in notmuch-show mode This binding is similar to mutt's, which is bind {mode} b "bounce-message" # remail a message to another user where {mode} is 'index', 'pager' or 'attach'. --- emacs/notmuch-show.el | 1 + 1 file changed, 1 insertion(+) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index 4b8c66fd..f33096cf 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -1418,6 +1418,7 @@ reset based on the original query." (define-key map (kbd "TAB") 'notmuch-show-next-button) (define-key map "f" 'notmuch-show-forward-message) (define-key map "F" 'notmuch-show-forward-open-messages) + (define-key map "b" 'notmuch-show-resend-message) (define-key map "l" 'notmuch-show-filter-thread) (define-key map "r" 'notmuch-show-reply-sender) (define-key map "R" 'notmuch-show-reply) From b183f2635eaf323066f26c7ca22d931d4f4ccd3a Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Fri, 6 May 2016 18:41:57 +0300 Subject: [PATCH 019/179] test: copyright information updates Files in test directories had only copyright of a single individual, of which code was adapted here as a base of the test system. Since then many Notmuch Developers have contributed to the test system, which is now acknowledged with a constant string in some of the test files. The README file in test directory instructed new files contain a copyright notice, but that has never been done (and it is also not needed). To simplify things a bit (and lessen confusion) this instruction is now removed. As a side enchangement, all of the 3 entries in the whole source tree cd'ing to `dirname` of "$0" now uses syntax cd "$(dirname "$0")". This makes these particular lines work when current working directory is e.g. /c/Program Files/notmuch/test/. (Probably it would fail elsewhere, though.) --- performance-test/notmuch-memory-test | 3 ++- performance-test/notmuch-time-test | 3 ++- test/README | 6 +----- test/notmuch-test | 3 ++- test/test-lib-common.sh | 1 + test/test-lib.sh | 1 + 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/performance-test/notmuch-memory-test b/performance-test/notmuch-memory-test index 3cf28c7f..047aac70 100755 --- a/performance-test/notmuch-memory-test +++ b/performance-test/notmuch-memory-test @@ -3,6 +3,7 @@ # Run tests # # Copyright (c) 2005 Junio C Hamano +# Copyright (c) 2010 Notmuch Developers # # Adapted from a Makefile to a shell script by Carl Worth (2010) @@ -14,7 +15,7 @@ if [ ${BASH_VERSINFO[0]} -lt 4 ]; then exit 1 fi -cd $(dirname "$0") +cd "$(dirname "$0")" for test in M*.sh; do ./"$test" "$@" diff --git a/performance-test/notmuch-time-test b/performance-test/notmuch-time-test index 7113efbf..4dd21fe1 100755 --- a/performance-test/notmuch-time-test +++ b/performance-test/notmuch-time-test @@ -3,6 +3,7 @@ # Run tests # # Copyright (c) 2005 Junio C Hamano +# Copyright (c) 2010 Notmuch Developers # # Adapted from a Makefile to a shell script by Carl Worth (2010) @@ -14,7 +15,7 @@ if [ ${BASH_VERSINFO[0]} -lt 4 ]; then exit 1 fi -cd $(dirname "$0") +cd "$(dirname "$0")" for test in T*.sh; do ./"$test" "$@" diff --git a/test/README b/test/README index bd9ab547..104a120e 100644 --- a/test/README +++ b/test/README @@ -145,13 +145,9 @@ Tddd-testname.sh where 'ddd' is three digits and 'testname' the "bare" name of your test. Tests will be run in order the 'ddd' part determines. The test script should start with the standard "#!/usr/bin/env bash" -with copyright notices, and an assignment to variable 'test_description', -like this: +and an assignment to variable 'test_description', like this: #!/usr/bin/env bash - # - # Copyright (c) 2005 Junio C Hamano - # test_description='xxx test (option --frotz) diff --git a/test/notmuch-test b/test/notmuch-test index b8437127..988dce6c 100755 --- a/test/notmuch-test +++ b/test/notmuch-test @@ -3,6 +3,7 @@ # Run tests # # Copyright (c) 2005 Junio C Hamano +# Copyright (c) 2010 Notmuch Developers # # Adapted from a Makefile to a shell script by Carl Worth (2010) @@ -14,7 +15,7 @@ if [ ${BASH_VERSINFO[0]} -lt 4 ]; then exit 1 fi -cd $(dirname "$0") +cd "$(dirname "$0")" TESTS=${NOTMUCH_TESTS:-`echo T[0-9][0-9][0-9]-*.sh`} diff --git a/test/test-lib-common.sh b/test/test-lib-common.sh index 4e17b781..ba4a8e11 100644 --- a/test/test-lib-common.sh +++ b/test/test-lib-common.sh @@ -1,5 +1,6 @@ # # Copyright (c) 2005 Junio C Hamano +# Copyright (c) 2010 Notmuch Developers # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/test/test-lib.sh b/test/test-lib.sh index 09f87319..62e123d6 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -1,5 +1,6 @@ # # Copyright (c) 2005 Junio C Hamano +# Copyright (c) 2010 Notmuch Developers # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From 7e6e23c36e290d4b22b0449766a6ef2107f1ef6c Mon Sep 17 00:00:00 2001 From: Ludovic LANGE Date: Sun, 6 Mar 2016 07:56:28 -0400 Subject: [PATCH 020/179] ruby: add bindings for `notmuch_database_get_all_tags` The Ruby bindings were missing a way to get all the tags of the database. Now you should be able to access this with the public instance method `all_tags` of your database object. Example of use: notmuchdb = Notmuch::Database.new path, { :create => false, :mode => Notmuch::MODE_READ_ONLY } my_tags = notmuchdb.all_tags my_tags.each { |tag| print tag } my_tags.destroy! Amended by db: improve error reporting, add test --- NEWS | 8 ++++++++ bindings/ruby/database.c | 24 ++++++++++++++++++++++++ bindings/ruby/defs.h | 3 +++ bindings/ruby/init.c | 1 + test/T395-ruby.sh | 16 ++++++++++++++++ 5 files changed, 52 insertions(+) diff --git a/NEWS b/NEWS index c945c245..90e233bf 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,11 @@ +Notmuch 0.23 (UNRELEASED) +========================= + +Ruby Bindings +------------- + +Add support for `notmuch_database_get_all_tags` + Notmuch 0.22 (2016-04-26) ========================= diff --git a/bindings/ruby/database.c b/bindings/ruby/database.c index c03d7011..6deda575 100644 --- a/bindings/ruby/database.c +++ b/bindings/ruby/database.c @@ -374,6 +374,30 @@ notmuch_rb_database_find_message_by_filename (VALUE self, VALUE pathv) return Qnil; } +/* + * call-seq: DB.get_all_tags() => TAGS + * + * Returns a list of all tags found in the database. + */ +VALUE +notmuch_rb_database_get_all_tags (VALUE self) +{ + notmuch_database_t *db; + notmuch_tags_t *tags; + + Data_Get_Notmuch_Database (self, db); + + tags = notmuch_database_get_all_tags (db); + if (!tags) { + const char *msg = notmuch_database_status_string (db); + if (!msg) + msg = "Unknown notmuch error"; + + rb_raise (notmuch_rb_eBaseError, "%s", msg); + } + return Data_Wrap_Struct (notmuch_rb_cTags, NULL, NULL, tags); +} + /* * call-seq: DB.query(query) => QUERY * diff --git a/bindings/ruby/defs.h b/bindings/ruby/defs.h index f4901a04..167250e4 100644 --- a/bindings/ruby/defs.h +++ b/bindings/ruby/defs.h @@ -177,6 +177,9 @@ notmuch_rb_database_find_message (VALUE self, VALUE idv); VALUE notmuch_rb_database_find_message_by_filename (VALUE self, VALUE pathv); +VALUE +notmuch_rb_database_get_all_tags (VALUE self); + VALUE notmuch_rb_database_query_create (VALUE self, VALUE qstrv); diff --git a/bindings/ruby/init.c b/bindings/ruby/init.c index ab3f22df..865d6bf6 100644 --- a/bindings/ruby/init.c +++ b/bindings/ruby/init.c @@ -229,6 +229,7 @@ Init_notmuch (void) notmuch_rb_database_find_message, 1); /* in database.c */ rb_define_method (notmuch_rb_cDatabase, "find_message_by_filename", notmuch_rb_database_find_message_by_filename, 1); /* in database.c */ + rb_define_method (notmuch_rb_cDatabase, "all_tags", notmuch_rb_database_get_all_tags, 0); /* in database.c */ rb_define_method (notmuch_rb_cDatabase, "query", notmuch_rb_database_query_create, 1); /* in database.c */ /* diff --git a/test/T395-ruby.sh b/test/T395-ruby.sh index d5cade8f..20e06917 100755 --- a/test/T395-ruby.sh +++ b/test/T395-ruby.sh @@ -83,4 +83,20 @@ EOF notmuch count --output=threads tag:inbox > EXPECTED test_expect_equal_file OUTPUT EXPECTED +test_begin_subtest "get all tags" +test_ruby <<"EOF" +require 'notmuch' +$maildir = ENV['MAIL_DIR'] +if not $maildir then + abort('environment variable MAIL_DIR must be set') +end +@db = Notmuch::Database.new($maildir) +@t = @db.all_tags() +for tag in @t do + print tag,"\n" +end +EOF +notmuch search --output=tags '*' > EXPECTED +test_expect_equal_file OUTPUT EXPECTED + test_done From 3458e3c89c1224d3e2dff7230780589ee3ee4c70 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 22 Mar 2016 07:54:47 -0300 Subject: [PATCH 021/179] lib: provide config API This is a thin wrapper around the Xapian metadata API. The job of this layer is to keep the config key value pairs from colliding with other metadata by transparently prefixing the keys, along with the usual glue to provide a C interface. The split of _get_config into two functions is to allow returning of the return value with different memory ownership semantics. --- lib/Makefile.local | 1 + lib/config.cc | 87 ++++++++++++++++++++++++++++++++++++++++++ lib/notmuch.h | 20 ++++++++++ test/T590-libconfig.sh | 58 ++++++++++++++++++++++++++++ 4 files changed, 166 insertions(+) create mode 100644 lib/config.cc create mode 100755 test/T590-libconfig.sh diff --git a/lib/Makefile.local b/lib/Makefile.local index 36c39243..76b57cb2 100644 --- a/lib/Makefile.local +++ b/lib/Makefile.local @@ -49,6 +49,7 @@ libnotmuch_cxx_srcs = \ $(dir)/index.cc \ $(dir)/message.cc \ $(dir)/query.cc \ + $(dir)/config.cc \ $(dir)/thread.cc libnotmuch_modules := $(libnotmuch_c_srcs:.c=.o) $(libnotmuch_cxx_srcs:.cc=.o) diff --git a/lib/config.cc b/lib/config.cc new file mode 100644 index 00000000..9ca74bf5 --- /dev/null +++ b/lib/config.cc @@ -0,0 +1,87 @@ +/* config.cc - API for database metadata + * + * Copyright © 2016 David Bremner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/ . + * + * Author: David Bremner + */ + +#include "notmuch.h" +#include "notmuch-private.h" +#include "database-private.h" + +static const std::string CONFIG_PREFIX = "C"; + +notmuch_status_t +notmuch_database_set_config (notmuch_database_t *notmuch, + const char *key, + const char *value) +{ + notmuch_status_t status; + Xapian::WritableDatabase *db; + + status = _notmuch_database_ensure_writable (notmuch); + if (status) + return status; + + try { + db = static_cast (notmuch->xapian_db); + db->set_metadata (CONFIG_PREFIX + key, value); + } catch (const Xapian::Error &error) { + status = NOTMUCH_STATUS_XAPIAN_EXCEPTION; + notmuch->exception_reported = TRUE; + _notmuch_database_log (notmuch, "Error: A Xapian exception occurred setting metadata: %s\n", + error.get_msg().c_str()); + } + return NOTMUCH_STATUS_SUCCESS; +} + +static notmuch_status_t +_metadata_value (notmuch_database_t *notmuch, + const char *key, + std::string &value) +{ + notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; + + try { + value = notmuch->xapian_db->get_metadata (CONFIG_PREFIX + key); + } catch (const Xapian::Error &error) { + status = NOTMUCH_STATUS_XAPIAN_EXCEPTION; + notmuch->exception_reported = TRUE; + _notmuch_database_log (notmuch, "Error: A Xapian exception occurred getting metadata: %s\n", + error.get_msg().c_str()); + } + return status; +} + +notmuch_status_t +notmuch_database_get_config (notmuch_database_t *notmuch, + const char *key, + char **value) +{ + std::string strval; + notmuch_status_t status; + + if (! value) + return NOTMUCH_STATUS_NULL_POINTER; + + status = _metadata_value (notmuch, key, strval); + if (status) + return status; + + *value = strdup (strval.c_str ()); + + return NOTMUCH_STATUS_SUCCESS; +} diff --git a/lib/notmuch.h b/lib/notmuch.h index 3a092efc..c827e02d 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -1838,6 +1838,26 @@ notmuch_filenames_move_to_next (notmuch_filenames_t *filenames); void notmuch_filenames_destroy (notmuch_filenames_t *filenames); + +/** + * set config 'key' to 'value' + * + */ +notmuch_status_t +notmuch_database_set_config (notmuch_database_t *db, const char *key, const char *value); + +/** + * retrieve config item 'key', assign to 'value' + * + * keys which have not been previously set with n_d_set_config will + * return an empty string. + * + * return value is allocated by malloc and should be freed by the + * caller. + */ +notmuch_status_t +notmuch_database_get_config (notmuch_database_t *db, const char *key, char **value); + /** * interrogate the library for compile time features */ diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh new file mode 100755 index 00000000..85e44979 --- /dev/null +++ b/test/T590-libconfig.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +test_description="library config API" + +. ./test-lib.sh || exit 1 + +add_email_corpus + +cat < c_head +#include +#include +#include +#include + +void run(int line, notmuch_status_t ret) +{ + if (ret) { + fprintf (stderr, "line %d: %s\n", line, ret); + exit (1); + } +} + +#define RUN(v) run(__LINE__, v); + +int main (int argc, char** argv) +{ + notmuch_database_t *db; + char *val; + notmuch_status_t stat; + + RUN(notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db)); + +EOF + +cat < c_tail + RUN(notmuch_database_destroy(db)); +} +EOF + +test_begin_subtest "notmuch_database_{set,get}_config" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +{ + RUN(notmuch_database_set_config (db, "testkey1", "testvalue1")); + RUN(notmuch_database_set_config (db, "testkey2", "testvalue2")); + RUN(notmuch_database_get_config (db, "testkey1", &val)); + printf("testkey1 = %s\n", val); + RUN(notmuch_database_get_config (db, "testkey2", &val)); + printf("testkey2 = %s\n", val); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +testkey1 = testvalue1 +testkey2 = testvalue2 +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_done From 92e59568fa4eec466db78af6c21c39ca5826b0d7 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 22 Mar 2016 07:54:48 -0300 Subject: [PATCH 022/179] lib: config list iterators Since xapian provides the ability to restrict the iterator to a given prefix, we expose this ability to the user. Otherwise we mimic the other iterator interfances in notmuch (e.g. tags.c). --- lib/config.cc | 106 +++++++++++++++++++++++++++++++++++++++++ lib/notmuch.h | 44 +++++++++++++++++ test/T590-libconfig.sh | 60 +++++++++++++++++++++++ 3 files changed, 210 insertions(+) diff --git a/lib/config.cc b/lib/config.cc index 9ca74bf5..e05e50fe 100644 --- a/lib/config.cc +++ b/lib/config.cc @@ -24,6 +24,21 @@ static const std::string CONFIG_PREFIX = "C"; +struct _notmuch_config_list { + notmuch_database_t *notmuch; + Xapian::TermIterator iterator; + char *current_key; + char *current_val; +}; + +static int +_notmuch_config_list_destroy (notmuch_config_list_t *list) +{ + /* invoke destructor w/o deallocating memory */ + list->iterator.~TermIterator(); + return 0; +} + notmuch_status_t notmuch_database_set_config (notmuch_database_t *notmuch, const char *key, @@ -85,3 +100,94 @@ notmuch_database_get_config (notmuch_database_t *notmuch, return NOTMUCH_STATUS_SUCCESS; } + +notmuch_status_t +notmuch_database_get_config_list (notmuch_database_t *notmuch, + const char *prefix, + notmuch_config_list_t **out) +{ + notmuch_config_list_t *list = NULL; + notmuch_status_t status = NOTMUCH_STATUS_SUCCESS; + + list = talloc (notmuch, notmuch_config_list_t); + if (! list) { + status = NOTMUCH_STATUS_OUT_OF_MEMORY; + goto DONE; + } + + talloc_set_destructor (list, _notmuch_config_list_destroy); + list->notmuch = notmuch; + list->current_key = NULL; + list->current_val = NULL; + + try { + + new(&(list->iterator)) Xapian::TermIterator (notmuch->xapian_db->metadata_keys_begin + (CONFIG_PREFIX + (prefix ? prefix : ""))); + + } catch (const Xapian::Error &error) { + _notmuch_database_log (notmuch, "A Xapian exception occurred getting metadata iterator: %s.\n", + error.get_msg().c_str()); + notmuch->exception_reported = TRUE; + status = NOTMUCH_STATUS_XAPIAN_EXCEPTION; + } + + *out = list; + + DONE: + if (status && list) + talloc_free (list); + + return status; +} + +notmuch_bool_t +notmuch_config_list_valid (notmuch_config_list_t *metadata) +{ + if (metadata->iterator == metadata->notmuch->xapian_db->metadata_keys_end ()) + return FALSE; + + return TRUE; +} + +const char * +notmuch_config_list_key (notmuch_config_list_t *list) +{ + if (list->current_key) + talloc_free (list->current_key); + + list->current_key = talloc_strdup (list, (*list->iterator).c_str () + CONFIG_PREFIX.length ()); + + return list->current_key; +} + +const char * +notmuch_config_list_value (notmuch_config_list_t *list) +{ + std::string strval; + notmuch_status_t status; + const char *key = notmuch_config_list_key (list); + + /* TODO: better error reporting?? */ + status = _metadata_value (list->notmuch, key, strval); + if (status) + return NULL; + + if (list->current_val) + talloc_free (list->current_val); + + list->current_val = talloc_strdup (list, strval.c_str ()); + return list->current_val; +} + +void +notmuch_config_list_move_to_next (notmuch_config_list_t *list) +{ + list->iterator++; +} + +void +notmuch_config_list_destroy (notmuch_config_list_t *list) +{ + talloc_free (list); +} diff --git a/lib/notmuch.h b/lib/notmuch.h index c827e02d..bd977c37 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -206,6 +206,7 @@ typedef struct _notmuch_message notmuch_message_t; typedef struct _notmuch_tags notmuch_tags_t; typedef struct _notmuch_directory notmuch_directory_t; typedef struct _notmuch_filenames notmuch_filenames_t; +typedef struct _notmuch_config_list notmuch_config_list_t; #endif /* __DOXYGEN__ */ /** @@ -1858,6 +1859,49 @@ notmuch_database_set_config (notmuch_database_t *db, const char *key, const char notmuch_status_t notmuch_database_get_config (notmuch_database_t *db, const char *key, char **value); +/** + * Create an iterator for all config items with keys matching a given prefix + */ +notmuch_status_t +notmuch_database_get_config_list (notmuch_database_t *db, const char *prefix, notmuch_config_list_t **out); + +/** + * Is 'config_list' iterator valid (i.e. _key, _value, _move_to_next can be called). + */ +notmuch_bool_t +notmuch_config_list_valid (notmuch_config_list_t *config_list); + +/** + * return key for current config pair + * + * return value is owned by the iterator, and will be destroyed by the + * next call to notmuch_config_list_key or notmuch_config_list_destroy. + */ +const char * +notmuch_config_list_key (notmuch_config_list_t *config_list); + +/** + * return 'value' for current config pair + * + * return value is owned by the iterator, and will be destroyed by the + * next call to notmuch_config_list_value or notmuch config_list_destroy + */ +const char * +notmuch_config_list_value (notmuch_config_list_t *config_list); + + +/** + * move 'config_list' iterator to the next pair + */ +void +notmuch_config_list_move_to_next (notmuch_config_list_t *config_list); + +/** + * free any resources held by 'config_list' + */ +void +notmuch_config_list_destroy (notmuch_config_list_t *config_list); + /** * interrogate the library for compile time features */ diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh index 85e44979..8ca68833 100755 --- a/test/T590-libconfig.sh +++ b/test/T590-libconfig.sh @@ -55,4 +55,64 @@ testkey2 = testvalue2 EOF test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "notmuch_database_get_config_list: empty list" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +{ + notmuch_config_list_t *list; + RUN(notmuch_database_get_config_list (db, "nonexistent", &list)); + printf("valid = %d\n", notmuch_config_list_valid (list)); + notmuch_config_list_destroy (list); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +valid = 0 +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT + + +test_begin_subtest "notmuch_database_get_config_list: all pairs" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +{ + notmuch_config_list_t *list; + RUN(notmuch_database_set_config (db, "zzzafter", "afterval")); + RUN(notmuch_database_set_config (db, "aaabefore", "beforeval")); + RUN(notmuch_database_get_config_list (db, "", &list)); + for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) { + printf("%s %s\n", notmuch_config_list_key (list), notmuch_config_list_value(list)); + } + notmuch_config_list_destroy (list); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +aaabefore beforeval +testkey1 testvalue1 +testkey2 testvalue2 +zzzafter afterval +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "notmuch_database_get_config_list: one prefix" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +{ + notmuch_config_list_t *list; + RUN(notmuch_database_get_config_list (db, "testkey", &list)); + for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) { + printf("%s %s\n", notmuch_config_list_key (list), notmuch_config_list_value(list)); + } + notmuch_config_list_destroy (list); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +testkey1 testvalue1 +testkey2 testvalue2 +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT + test_done From e042a25a3fd8706b6e1ee7bdf527bb17ecb74c6a Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 22 Mar 2016 07:54:50 -0300 Subject: [PATCH 023/179] CLI: add optional config data to dump output. Note that it changes the default dump output format, but doesn't break existing notmuch-restore. It might break user scripts though. --- doc/man1/notmuch-dump.rst | 17 +++++++++ notmuch-client.h | 8 ++++ notmuch-dump.c | 78 +++++++++++++++++++++++++++++++++++++-- notmuch-new.c | 2 +- test/T150-tagging.sh | 8 ++-- test/T240-dump-restore.sh | 14 +++---- test/T590-libconfig.sh | 17 +++++++++ test/test-lib.sh | 6 +++ 8 files changed, 135 insertions(+), 15 deletions(-) diff --git a/doc/man1/notmuch-dump.rst b/doc/man1/notmuch-dump.rst index a37c337c..eda9e07f 100644 --- a/doc/man1/notmuch-dump.rst +++ b/doc/man1/notmuch-dump.rst @@ -71,6 +71,23 @@ Supported options for **dump** include characters. Note also that tags with spaces will not be correctly restored with this format. + ``--include=(config|tags)`` + + Control what kind of metadata is included in the output. + + **config** + + Output configuration data stored in the database. Each line + starts with "#@ ", followed by a space seperated key-value + pair. Both key and value are hex encoded if needed. + + **tags** + + Output per-message metadata, namely tags. See *format* above + for description of the output. + + The default is to include both tags and configuration information + ``--output=``\ Write output to given file instead of stdout. diff --git a/notmuch-client.h b/notmuch-client.h index b3d0b668..c130b937 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -446,11 +446,19 @@ typedef enum dump_formats { DUMP_FORMAT_SUP } dump_format_t; +typedef enum dump_includes { + DUMP_INCLUDE_TAGS = 1, + DUMP_INCLUDE_CONFIG = 2, +} dump_include_t; + +#define NOTMUCH_DUMP_VERSION 2 + int notmuch_database_dump (notmuch_database_t *notmuch, const char *output_file_name, const char *query_str, dump_format_t output_format, + dump_include_t include, notmuch_bool_t gzip_output); /* If status is non-zero (i.e. error) print appropriate diff --git a/notmuch-dump.c b/notmuch-dump.c index 829781f8..8e795115 100644 --- a/notmuch-dump.c +++ b/notmuch-dump.c @@ -23,16 +23,80 @@ #include "string-util.h" #include +static int +database_dump_config (notmuch_database_t *notmuch, gzFile output) +{ + notmuch_config_list_t *list; + int ret = EXIT_FAILURE; + char *buffer = NULL; + size_t buffer_size = 0; + + if (print_status_database ("notmuch dump", notmuch, + notmuch_database_get_config_list (notmuch, NULL, &list))) + goto DONE; + + for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) { + if (hex_encode (notmuch, notmuch_config_list_key (list), + &buffer, &buffer_size) != HEX_SUCCESS) { + fprintf (stderr, "Error: failed to hex-encode config key %s\n", + notmuch_config_list_key (list)); + goto DONE; + } + gzprintf (output, "#@ %s", buffer); + + if (hex_encode (notmuch, notmuch_config_list_value (list), + &buffer, &buffer_size) != HEX_SUCCESS) { + fprintf (stderr, "Error: failed to hex-encode config value %s\n", + notmuch_config_list_value (list) ); + goto DONE; + } + + gzprintf (output, " %s\n", buffer); + } + + ret = EXIT_SUCCESS; + + DONE: + if (list) + notmuch_config_list_destroy (list); + + if (buffer) + talloc_free (buffer); + + return ret; +} + +static void +print_dump_header (gzFile output, int output_format, int include) +{ + gzprintf (output, "#notmuch-dump %s:%d %s%s%s\n", + (output_format == DUMP_FORMAT_SUP) ? "sup" : "batch-tag", + NOTMUCH_DUMP_VERSION, + (include & DUMP_INCLUDE_CONFIG) ? "config" : "", + (include & DUMP_INCLUDE_TAGS) && (include & DUMP_INCLUDE_CONFIG) ? "," : "", + (include & DUMP_INCLUDE_TAGS) ? "tags" : ""); +} static int database_dump_file (notmuch_database_t *notmuch, gzFile output, - const char *query_str, int output_format) + const char *query_str, int output_format, int include) { notmuch_query_t *query; notmuch_messages_t *messages; notmuch_message_t *message; notmuch_tags_t *tags; + print_dump_header (output, output_format, include); + + if (include & DUMP_INCLUDE_CONFIG) { + if (print_status_database ("notmuch dump", notmuch, + database_dump_config(notmuch,output))) + return EXIT_FAILURE; + } + + if (! (include & DUMP_INCLUDE_TAGS)) + return EXIT_SUCCESS; + if (! query_str) query_str = ""; @@ -130,6 +194,7 @@ notmuch_database_dump (notmuch_database_t *notmuch, const char *output_file_name, const char *query_str, dump_format_t output_format, + dump_include_t include, notmuch_bool_t gzip_output) { gzFile output = NULL; @@ -164,7 +229,7 @@ notmuch_database_dump (notmuch_database_t *notmuch, goto DONE; } - ret = database_dump_file (notmuch, output, query_str, output_format); + ret = database_dump_file (notmuch, output, query_str, output_format, include); if (ret) goto DONE; ret = gzflush (output, Z_FINISH); @@ -226,6 +291,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[]) int opt_index; int output_format = DUMP_FORMAT_BATCH_TAG; + int include = 0; notmuch_bool_t gzip_output = 0; notmuch_opt_desc_t options[] = { @@ -233,6 +299,9 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[]) (notmuch_keyword_t []){ { "sup", DUMP_FORMAT_SUP }, { "batch-tag", DUMP_FORMAT_BATCH_TAG }, { 0, 0 } } }, + { NOTMUCH_OPT_KEYWORD_FLAGS, &include, "include", 'I', + (notmuch_keyword_t []){ { "config", DUMP_INCLUDE_CONFIG }, + { "tags", DUMP_INCLUDE_TAGS} } }, { NOTMUCH_OPT_STRING, &output_file_name, "output", 'o', 0 }, { NOTMUCH_OPT_BOOLEAN, &gzip_output, "gzip", 'z', 0 }, { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 }, @@ -245,6 +314,9 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_process_shared_options (argv[0]); + if (include == 0) + include = DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS; + if (opt_index < argc) { query_str = query_string_from_args (notmuch, argc - opt_index, argv + opt_index); if (query_str == NULL) { @@ -254,7 +326,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[]) } ret = notmuch_database_dump (notmuch, output_file_name, query_str, - output_format, gzip_output); + output_format, include, gzip_output); notmuch_database_destroy (notmuch); diff --git a/notmuch-new.c b/notmuch-new.c index 2d975eb5..8d1545e1 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -1042,7 +1042,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[]) } if (notmuch_database_dump (notmuch, backup_name, "", - DUMP_FORMAT_BATCH_TAG, TRUE)) { + DUMP_FORMAT_BATCH_TAG, DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS, TRUE)) { fprintf (stderr, "Backup failed. Aborting upgrade."); return EXIT_FAILURE; } diff --git a/test/T150-tagging.sh b/test/T150-tagging.sh index a451ffae..61d13116 100755 --- a/test/T150-tagging.sh +++ b/test/T150-tagging.sh @@ -188,7 +188,7 @@ cat < EXPECTED +%22%27%22%27%22%22%27%27 +inbox +tag4 +tag5 +unread -- id:msg-002@notmuch-test-suite EOF -notmuch dump --format=batch-tag | sort > OUTPUT +NOTMUCH_DUMP_TAGS > OUTPUT notmuch restore --format=batch-tag < BACKUP test_expect_equal_file EXPECTED OUTPUT @@ -209,7 +209,7 @@ cat < EXPECTED +%21@%23%20%24%25%5e%26%2a%29-_=+%5b%7b%5c%20%7c%3b%3a%27%20%22,.%3c%60%7e +inbox +tag5 +unread -- id:msg-001@notmuch-test-suite EOF -notmuch dump --format=batch-tag | sort > OUTPUT +NOTMUCH_DUMP_TAGS > OUTPUT notmuch restore --format=batch-tag < BACKUP test_expect_equal_file EXPECTED OUTPUT @@ -235,7 +235,7 @@ cat < EXPECTED +%2a@%7d%cf%b5%f4%85%80%adO3%da%a7 +=%e0%ac%95%c8%b3+%ef%aa%95%c8%a64w%c7%9d%c9%a2%cf%b3%d6%82%24B%c4%a9%c5%a1UX%ee%99%b0%27E7%ca%a4%d0%8b%5d +A%e1%a0%bc%de%8b%d5%b2V%d9%9b%f3%b5%a2%a3M%d8%a1u@%f0%a0%ac%948%7e%f0%ab%86%af%27 +L%df%85%ef%a1%a5m@%d3%96%c2%ab%d4%9f%ca%b8%f3%b3%a2%bf%c7%b1_u%d7%b4%c7%b1 +P%c4%98%2f +R +inbox +tag5 +unread +%7e%d1%8b%25%ec%a0%ae%d1%a0M%3b%e3%b6%b7%e9%a4%87%3c%db%9a%cc%a8%e1%96%9d +%c4%bf7%c7%ab9H%c4%99k%ea%91%bd%c3%8ck%e2%b3%8dk%c5%952V%e4%99%b2%d9%b3%e4%8b%bda%5b%24%c7%9b +%da%88=f%cc%b9I%ce%af%7b%c9%97%e3%b9%8bH%cb%92X%d2%8c6 +%dc%9crh%d2%86B%e5%97%a2%22t%ed%99%82d -- id:msg-001@notmuch-test-suite EOF -notmuch dump --format=batch-tag | sort > OUTPUT +NOTMUCH_DUMP_TAGS > OUTPUT notmuch restore --format=batch-tag < BACKUP test_expect_equal_file EXPECTED OUTPUT @@ -260,7 +260,7 @@ cat < EXPECTED +foo%3a%3abar%25 +found%3a%3ait +inbox +tag5 +unread +winner -- id:msg-001@notmuch-test-suite EOF -notmuch dump --format=batch-tag | sort > OUTPUT +NOTMUCH_DUMP_TAGS > OUTPUT notmuch restore --format=batch-tag < BACKUP test_expect_equal_file EXPECTED OUTPUT diff --git a/test/T240-dump-restore.sh b/test/T240-dump-restore.sh index e6976ff8..bbfb09b9 100755 --- a/test/T240-dump-restore.sh +++ b/test/T240-dump-restore.sh @@ -97,7 +97,7 @@ test_expect_equal_file dump.expected dump.actual # Note, we assume all messages from cworth have a message-id # containing cworth.org -grep 'cworth[.]org' dump.expected > dump-cworth.expected +{ head -1 dump.expected ; grep 'cworth[.]org' dump.expected; } > dump-cworth.expected test_begin_subtest "dump -- from:cworth" notmuch dump -- from:cworth > dump-dash-cworth.actual @@ -118,16 +118,16 @@ notmuch search --output=messages from:cworth | sed s/^id:// |\ test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "format=batch-tag, dump sanity check." -notmuch dump --format=sup from:cworth | cut -f1 -d' ' | \ +NOTMUCH_DUMP_TAGS --format=sup from:cworth | cut -f1 -d' ' | \ sort > EXPECTED.$test_count -notmuch dump --format=batch-tag from:cworth | sed 's/^.*-- id://' | \ +NOTMUCH_DUMP_TAGS --format=batch-tag from:cworth | sed 's/^.*-- id://' | \ sort > OUTPUT.$test_count test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count test_begin_subtest "format=batch-tag, missing newline" printf "+a_tag_without_newline -- id:20091117232137.GA7669@griffis1.net" > IN notmuch restore --accumulate < IN -notmuch dump id:20091117232137.GA7669@griffis1.net > OUT +NOTMUCH_DUMP_TAGS id:20091117232137.GA7669@griffis1.net > OUT cat < EXPECTED +a_tag_without_newline +inbox +unread -- id:20091117232137.GA7669@griffis1.net EOF @@ -155,7 +155,7 @@ cat <EXPECTED.$test_count + -- id:20091117232137.GA7669@griffis1.net EOF notmuch restore --format=batch-tag < EXPECTED.$test_count -notmuch dump --format=batch-tag id:20091117232137.GA7669@griffis1.net > OUTPUT.$test_count +NOTMUCH_DUMP_TAGS --format=batch-tag id:20091117232137.GA7669@griffis1.net > OUTPUT.$test_count test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count tag1='comic_swear=$&^%$^%\\//-+$^%$' @@ -217,9 +217,9 @@ notmuch dump --format=batch-tag > OUTPUT.$test_count test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count test_begin_subtest 'format=batch-tag, checking encoded output' -notmuch dump --format=batch-tag -- from:cworth |\ +NOTMUCH_DUMP_TAGS --format=batch-tag -- from:cworth |\ awk "{ print \"+$enc1 +$enc2 +$enc3 -- \" \$5 }" > EXPECTED.$test_count -notmuch dump --format=batch-tag -- from:cworth > OUTPUT.$test_count +NOTMUCH_DUMP_TAGS --format=batch-tag -- from:cworth > OUTPUT.$test_count test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count test_begin_subtest 'restoring sane tags' diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh index 8ca68833..5ea53003 100755 --- a/test/T590-libconfig.sh +++ b/test/T590-libconfig.sh @@ -115,4 +115,21 @@ testkey2 testvalue2 EOF test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "dump config" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +{ + RUN(notmuch_database_set_config (db, "key with spaces", "value, with, spaces!")); +} +EOF +notmuch dump --include=config >OUTPUT +cat <<'EOF' >EXPECTED +#notmuch-dump batch-tag:2 config +#@ aaabefore beforeval +#@ key%20with%20spaces value,%20with,%20spaces%21 +#@ testkey1 testvalue1 +#@ testkey2 testvalue2 +#@ zzzafter afterval +EOF +test_expect_equal_file EXPECTED OUTPUT + test_done diff --git a/test/test-lib.sh b/test/test-lib.sh index 62e123d6..e4398620 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -674,6 +674,12 @@ NOTMUCH_NEW () notmuch new "${@}" | grep -v -E -e '^Processed [0-9]*( total)? file|Found [0-9]* total file' } +NOTMUCH_DUMP_TAGS () +{ + # this relies on the default format being batch-tag, otherwise some tests will break + notmuch dump --include=tags "${@}" | sed '/^#/d' | sort +} + notmuch_search_sanitize () { perl -pe 's/("?thread"?: ?)("?)................("?)/\1\2XXX\3/' From c6fcc555dde2a50ac779d5871720a4f074322457 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 22 Mar 2016 07:54:51 -0300 Subject: [PATCH 024/179] CLI: optionally restore config data. The default to restore config data seems safest, especially since currently we have no config data to mess up. --- doc/man1/notmuch-restore.rst | 18 ++++++++++++ notmuch-restore.c | 54 ++++++++++++++++++++++++++++++++++++ test/T590-libconfig.sh | 11 ++++++++ 3 files changed, 83 insertions(+) diff --git a/doc/man1/notmuch-restore.rst b/doc/man1/notmuch-restore.rst index 362e2629..87fa22ef 100644 --- a/doc/man1/notmuch-restore.rst +++ b/doc/man1/notmuch-restore.rst @@ -50,6 +50,24 @@ Supported options for **restore** include format, this heuristic, based the fact that batch-tag format contains no parentheses, should be accurate. + ``--include=(config|tags)`` + + Control what kind of metadata is restored. + + **config** + + Restore configuration data to the database. Each configuration line starts + with "#@ ", followed by a space seperated key-value pair. + Both key and value are hex encoded if needed. + + **tags** + + Output per-message metadata, namely tags. See *format* above + for more details. + + The default is to restore both tags and configuration + information + ``--input=``\ Read input from given file instead of stdin. diff --git a/notmuch-restore.c b/notmuch-restore.c index 9abc64fd..8ad67b5e 100644 --- a/notmuch-restore.c +++ b/notmuch-restore.c @@ -24,6 +24,39 @@ #include "string-util.h" #include "zlib-extra.h" +static int +process_config_line (notmuch_database_t *notmuch, const char* line) +{ + const char *key_p, *val_p; + char *key, *val; + size_t key_len,val_len; + const char *delim = " \t\n"; + int ret = EXIT_FAILURE; + + void *local = talloc_new(NULL); + + key_p = strtok_len_c (line, delim, &key_len); + val_p = strtok_len_c (key_p+key_len, delim, &val_len); + + key = talloc_strndup (local, key_p, key_len); + val = talloc_strndup (local, val_p, val_len); + if (hex_decode_inplace (key) != HEX_SUCCESS || + hex_decode_inplace (val) != HEX_SUCCESS ) { + fprintf (stderr, "hex decoding failure on line %s\n", line); + goto DONE; + } + + if (print_status_database ("notmuch restore", notmuch, + notmuch_database_set_config (notmuch, key, val))) + goto DONE; + + ret = EXIT_SUCCESS; + + DONE: + talloc_free (local); + return ret; +} + static regex_t regex; /* Non-zero return indicates an error in retrieving the message, @@ -137,6 +170,7 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[]) int ret = 0; int opt_index; + int include = 0; int input_format = DUMP_FORMAT_AUTO; if (notmuch_database_open (notmuch_config_get_database_path (config), @@ -152,6 +186,10 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[]) { "batch-tag", DUMP_FORMAT_BATCH_TAG }, { "sup", DUMP_FORMAT_SUP }, { 0, 0 } } }, + { NOTMUCH_OPT_KEYWORD_FLAGS, &include, "include", 'I', + (notmuch_keyword_t []){ { "config", DUMP_INCLUDE_CONFIG }, + { "tags", DUMP_INCLUDE_TAGS} } }, + { NOTMUCH_OPT_STRING, &input_file_name, "input", 'i', 0 }, { NOTMUCH_OPT_BOOLEAN, &accumulate, "accumulate", 'a', 0 }, { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 }, @@ -167,6 +205,10 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_process_shared_options (argv[0]); notmuch_exit_if_unmatched_db_uuid (notmuch); + if (include == 0) { + include = DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS; + } + name_for_error = input_file_name ? input_file_name : "stdin"; if (! accumulate) @@ -225,11 +267,23 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[]) ret = EXIT_FAILURE; goto DONE; } + + if ((include & DUMP_INCLUDE_CONFIG) && line_len >= 2 && line[0] == '#' && line[1] == '@') { + ret = process_config_line(notmuch, line+2); + if (ret) + goto DONE; + } + } while ((line_len == 0) || (line[0] == '#') || /* the cast is safe because we checked about for line_len < 0 */ (strspn (line, " \t\n") == (unsigned)line_len)); + if (! (include & DUMP_INCLUDE_TAGS)) { + ret = EXIT_SUCCESS; + goto DONE; + } + char *p; for (p = line; (input_format == DUMP_FORMAT_AUTO) && *p; p++) { if (*p == '(') diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh index 5ea53003..9c1e566d 100755 --- a/test/T590-libconfig.sh +++ b/test/T590-libconfig.sh @@ -132,4 +132,15 @@ cat <<'EOF' >EXPECTED EOF test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "restore config" +notmuch dump --include=config >EXPECTED +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +{ + RUN(notmuch_database_set_config (db, "testkey1", "mutatedvalue")); +} +EOF +notmuch restore --include=config OUTPUT +test_expect_equal_file EXPECTED OUTPUT + test_done From 2d2a13966c74ffe86fc10abfbe1ac4c9798788ce Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 22 Mar 2016 07:54:52 -0300 Subject: [PATCH 025/179] CLI: add notmuch-config support for named queries Most of the infrastructure here is general, only the validation/dispatch is hardcoded to a particular prefix. A notable change in behaviour is that notmuch-config now opens the database e.g. on every call to list, which fails with an error message if the database doesn't exit yet. --- doc/man1/notmuch-config.rst | 6 +++ notmuch-config.c | 88 ++++++++++++++++++++++++++++++++++++- test/Makefile.local | 2 +- test/T030-config.sh | 12 ++--- test/T600-named-queries.sh | 53 ++++++++++++++++++++++ test/test-lib.sh | 5 +++ 6 files changed, 159 insertions(+), 7 deletions(-) create mode 100755 test/T600-named-queries.sh diff --git a/doc/man1/notmuch-config.rst b/doc/man1/notmuch-config.rst index 26a8eb14..5a517ebd 100644 --- a/doc/man1/notmuch-config.rst +++ b/doc/man1/notmuch-config.rst @@ -138,6 +138,12 @@ The available configuration items are described below. "compact" (see **notmuch-compact(1)**) and "field_processor" (see **notmuch-search-terms(7)**). + **query.** + + Expansion for named query called . See + **notmuch-search-terms(7)** for more information about named + queries. + ENVIRONMENT =========== diff --git a/notmuch-config.c b/notmuch-config.c index 01bb1859..c618f2ca 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -751,6 +751,28 @@ _item_split (char *item, char **group, char **key) } #define BUILT_WITH_PREFIX "built_with." +#define QUERY_PREFIX "query." + +static int +_print_db_config(notmuch_config_t *config, const char *name) +{ + notmuch_database_t *notmuch; + char *val; + + if (notmuch_database_open (notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much)) + return EXIT_FAILURE; + + /* XXX Handle UUID mismatch? */ + + if (print_status_database ("notmuch config", notmuch, + notmuch_database_get_config (notmuch, name, &val))) + return EXIT_FAILURE; + + puts (val); + + return EXIT_SUCCESS; +} static int notmuch_config_command_get (notmuch_config_t *config, char *item) @@ -778,6 +800,8 @@ notmuch_config_command_get (notmuch_config_t *config, char *item) } else if (STRNCMP_LITERAL (item, BUILT_WITH_PREFIX) == 0) { printf ("%s\n", 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 { char **value; size_t i, length; @@ -804,6 +828,39 @@ notmuch_config_command_get (notmuch_config_t *config, char *item) return 0; } +static int +_set_db_config(notmuch_config_t *config, const char *key, int argc, char **argv) +{ + notmuch_database_t *notmuch; + const char *val = ""; + + if (argc > 1) { + /* XXX handle lists? */ + fprintf (stderr, "notmuch config set: at most one value expected for %s\n", key); + return EXIT_FAILURE; + } + + if (argc > 0) { + val = argv[0]; + } + + if (notmuch_database_open (notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_WRITE, ¬much)) + return EXIT_FAILURE; + + /* XXX Handle UUID mismatch? */ + + if (print_status_database ("notmuch config", notmuch, + notmuch_database_set_config (notmuch, key, val))) + return EXIT_FAILURE; + + if (print_status_database ("notmuch config", notmuch, + notmuch_database_close (notmuch))) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + static int notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char *argv[]) { @@ -814,6 +871,10 @@ notmuch_config_command_set (notmuch_config_t *config, char *item, int argc, char return 1; } + if (STRNCMP_LITERAL (item, QUERY_PREFIX) == 0) { + return _set_db_config (config, item, argc, argv); + } + if (_item_split (item, &group, &key)) return 1; @@ -852,6 +913,31 @@ _notmuch_config_list_built_with () notmuch_built_with ("field_processor") ? "true" : "false"); } +static int +_list_db_config (notmuch_config_t *config) +{ + notmuch_database_t *notmuch; + notmuch_config_list_t *list; + + if (notmuch_database_open (notmuch_config_get_database_path (config), + NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much)) + return EXIT_FAILURE; + + /* XXX Handle UUID mismatch? */ + + + if (print_status_database ("notmuch config", notmuch, + notmuch_database_get_config_list (notmuch, "", &list))) + return EXIT_FAILURE; + + for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) { + printf("%s=%s\n", notmuch_config_list_key (list), notmuch_config_list_value(list)); + } + notmuch_config_list_destroy (list); + + return EXIT_SUCCESS; +} + static int notmuch_config_command_list (notmuch_config_t *config) { @@ -888,7 +974,7 @@ notmuch_config_command_list (notmuch_config_t *config) g_strfreev (groups); _notmuch_config_list_built_with (); - return 0; + return _list_db_config (config); } int diff --git a/test/Makefile.local b/test/Makefile.local index 022f2cf7..91b36936 100644 --- a/test/Makefile.local +++ b/test/Makefile.local @@ -19,7 +19,7 @@ $(dir)/hex-xcode: $(dir)/hex-xcode.o command-line-arguments.o util/libutil.a $(call quiet,CC) $^ -o $@ $(LDFLAGS) $(TALLOC_LDFLAGS) random_corpus_deps = $(dir)/random-corpus.o $(dir)/database-test.o \ - notmuch-config.o command-line-arguments.o \ + notmuch-config.o status.o command-line-arguments.o \ lib/libnotmuch.a util/libutil.a \ parse-time-string/libparse-time-string.a diff --git a/test/T030-config.sh b/test/T030-config.sh index 437269ff..b8d5a86f 100755 --- a/test/T030-config.sh +++ b/test/T030-config.sh @@ -43,10 +43,10 @@ notmuch config set foo.nonexistent test_expect_equal "$(notmuch config get foo.nonexistent)" "" test_begin_subtest "List all items" -notmuch config set database.path "/canonical/path" -output=$(notmuch config list | notmuch_built_with_sanitize) -test_expect_equal "$output" "\ -database.path=/canonical/path +notmuch config list 2>&1 | notmuch_config_sanitize > OUTPUT +cat < EXPECTED +Error opening database at MAIL_DIR/.notmuch: No such file or directory +database.path=MAIL_DIR user.name=Notmuch Test Suite user.primary_email=test_suite@notmuchmail.org user.other_email=test_suite_other@notmuchmail.org;test_suite@otherdomain.org @@ -58,7 +58,9 @@ crypto.gpg_path=gpg foo.string=this is another string value foo.list=this;is another;list value; built_with.compact=something -built_with.field_processor=something" +built_with.field_processor=something +EOF +test_expect_equal_file EXPECTED OUTPUT test_begin_subtest "Top level --config=FILE option" cp "${NOTMUCH_CONFIG}" alt-config diff --git a/test/T600-named-queries.sh b/test/T600-named-queries.sh new file mode 100755 index 00000000..09226208 --- /dev/null +++ b/test/T600-named-queries.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +test_description='named queries' +. ./test-lib.sh || exit 1 + +QUERYSTR="date:2009-11-18..2009-11-18 and tag:unread" + +test_expect_code 1 "error adding named query before initializing DB" \ + "notmuch config set query.test \"$QUERYSTR\"" + +add_email_corpus + +test_expect_success "adding named query" \ + "notmuch config set query.test \"$QUERYSTR\"" + +QUERYSTR2="query:test and subject:Maildir" +test_expect_success "adding nested named query" \ + "notmuch config set query.test2 \"$QUERYSTR2\"" + +test_begin_subtest "retrieve named query" +output=$(notmuch config get query.test) +test_expect_equal "$QUERYSTR" "$output" + +test_begin_subtest "List all queries" +notmuch config list | grep ^query | notmuch_config_sanitize > OUTPUT +cat < EXPECTED +query.test=date:2009-11-18..2009-11-18 and tag:unread +query.test2=query:test and subject:Maildir +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "dump named queries" +notmuch dump | grep '^#@' > OUTPUT +cat< QUERIES.BEFORE +#@ query.test date%3a2009-11-18..2009-11-18%20and%20tag%3aunread +#@ query.test2 query%3atest%20and%20subject%3aMaildir +EOF +test_expect_equal_file QUERIES.BEFORE OUTPUT + +test_begin_subtest "delete named queries" +notmuch dump > BEFORE +notmuch config set query.test +notmuch dump | grep '^#@' > OUTPUT +cat< EXPECTED +#@ query.test2 query%3atest%20and%20subject%3aMaildir +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "restore named queries" +notmuch restore < BEFORE +notmuch dump | grep '^#@' > OUTPUT +test_expect_equal_file QUERIES.BEFORE OUTPUT + +test_done diff --git a/test/test-lib.sh b/test/test-lib.sh index e4398620..201d0ebb 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -746,6 +746,11 @@ notmuch_built_with_sanitize () sed 's/^built_with[.]\(.*\)=.*$/built_with.\1=something/' } +notmuch_config_sanitize () +{ + notmuch_dir_sanitize | notmuch_built_with_sanitize +} + # End of notmuch helper functions # Use test_set_prereq to tell that a particular prerequisite is available. From 30caaf52b022be5f0f695f3088286fb56e82c782 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 22 Mar 2016 07:54:53 -0300 Subject: [PATCH 026/179] lib: make a global constant for query parser flags It's already kindof gross that this is hardcoded in two different places. We will also need these later in field processors calling back into the query parser. --- lib/database-private.h | 7 +++++++ lib/query.cc | 16 ++-------------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/lib/database-private.h b/lib/database-private.h index e1962f43..d2990b6c 100644 --- a/lib/database-private.h +++ b/lib/database-private.h @@ -144,6 +144,13 @@ operator&=(_notmuch_features &a, _notmuch_features b) return a; } +#define NOTMUCH_QUERY_PARSER_FLAGS (Xapian::QueryParser::FLAG_BOOLEAN | \ + Xapian::QueryParser::FLAG_PHRASE | \ + Xapian::QueryParser::FLAG_LOVEHATE | \ + Xapian::QueryParser::FLAG_BOOLEAN_ANY_CASE | \ + Xapian::QueryParser::FLAG_WILDCARD | \ + Xapian::QueryParser::FLAG_PURE_NOT) + struct _notmuch_database { notmuch_bool_t exception_reported; diff --git a/lib/query.cc b/lib/query.cc index 77a7926b..0875b0e4 100644 --- a/lib/query.cc +++ b/lib/query.cc @@ -220,12 +220,6 @@ _notmuch_query_search_documents (notmuch_query_t *query, Xapian::Query string_query, final_query, exclude_query; Xapian::MSet mset; Xapian::MSetIterator iterator; - unsigned int flags = (Xapian::QueryParser::FLAG_BOOLEAN | - Xapian::QueryParser::FLAG_PHRASE | - Xapian::QueryParser::FLAG_LOVEHATE | - Xapian::QueryParser::FLAG_BOOLEAN_ANY_CASE | - Xapian::QueryParser::FLAG_WILDCARD | - Xapian::QueryParser::FLAG_PURE_NOT); if (strcmp (query_string, "") == 0 || strcmp (query_string, "*") == 0) @@ -233,7 +227,7 @@ _notmuch_query_search_documents (notmuch_query_t *query, final_query = mail_query; } else { string_query = notmuch->query_parser-> - parse_query (query_string, flags); + parse_query (query_string, NOTMUCH_QUERY_PARSER_FLAGS); final_query = Xapian::Query (Xapian::Query::OP_AND, mail_query, string_query); } @@ -579,12 +573,6 @@ _notmuch_query_count_documents (notmuch_query_t *query, const char *type, unsign type)); Xapian::Query string_query, final_query, exclude_query; Xapian::MSet mset; - unsigned int flags = (Xapian::QueryParser::FLAG_BOOLEAN | - Xapian::QueryParser::FLAG_PHRASE | - Xapian::QueryParser::FLAG_LOVEHATE | - Xapian::QueryParser::FLAG_BOOLEAN_ANY_CASE | - Xapian::QueryParser::FLAG_WILDCARD | - Xapian::QueryParser::FLAG_PURE_NOT); if (strcmp (query_string, "") == 0 || strcmp (query_string, "*") == 0) @@ -592,7 +580,7 @@ _notmuch_query_count_documents (notmuch_query_t *query, const char *type, unsign final_query = mail_query; } else { string_query = notmuch->query_parser-> - parse_query (query_string, flags); + parse_query (query_string, NOTMUCH_QUERY_PARSER_FLAGS); final_query = Xapian::Query (Xapian::Query::OP_AND, mail_query, string_query); } From b9bf3f44eacd42ce53885c79f9dad8d82c76f13d Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 22 Mar 2016 07:54:54 -0300 Subject: [PATCH 027/179] lib: add support for named queries This relies on the optional presense of xapian field processors, and the library config API. --- doc/man7/notmuch-search-terms.rst | 8 ++++++ lib/Makefile.local | 1 + lib/database-private.h | 1 + lib/database.cc | 3 +++ lib/query-fp.cc | 43 +++++++++++++++++++++++++++++++ lib/query-fp.h | 42 ++++++++++++++++++++++++++++++ test/T600-named-queries.sh | 17 ++++++++++++ 7 files changed, 115 insertions(+) create mode 100644 lib/query-fp.cc create mode 100644 lib/query-fp.h diff --git a/doc/man7/notmuch-search-terms.rst b/doc/man7/notmuch-search-terms.rst index adedf5a3..223031b8 100644 --- a/doc/man7/notmuch-search-terms.rst +++ b/doc/man7/notmuch-search-terms.rst @@ -56,6 +56,8 @@ indicate user-supplied values): - lastmod:.. +- query: + The **from:** prefix is used to match the name or address of the sender of an email message. @@ -132,6 +134,11 @@ were added/removed or filenames changed). This is usually used in conjunction with the **--uuid** argument to **notmuch search** to find messages that have changed since an earlier query. +The **query:** prefix allows queries to refer to previously saved +queries added with **notmuch-config(1)**. Named queries are only +available if notmuch is built with **Xapian Field Processors** (see +below). + Operators --------- @@ -385,6 +392,7 @@ notmuch was built against a sufficiently recent version of Xapian by running Currently the following features require field processor support: - non-range date queries, e.g. "date:today" +- named queries e.g. "query:my_special_query" SEE ALSO ======== diff --git a/lib/Makefile.local b/lib/Makefile.local index 76b57cb2..beb96358 100644 --- a/lib/Makefile.local +++ b/lib/Makefile.local @@ -49,6 +49,7 @@ libnotmuch_cxx_srcs = \ $(dir)/index.cc \ $(dir)/message.cc \ $(dir)/query.cc \ + $(dir)/query-fp.cc \ $(dir)/config.cc \ $(dir)/thread.cc diff --git a/lib/database-private.h b/lib/database-private.h index d2990b6c..1a78b60e 100644 --- a/lib/database-private.h +++ b/lib/database-private.h @@ -185,6 +185,7 @@ struct _notmuch_database { Xapian::ValueRangeProcessor *date_range_processor; #if HAVE_XAPIAN_FIELD_PROCESSOR Xapian::FieldProcessor *date_field_processor; + Xapian::FieldProcessor *query_field_processor; #endif Xapian::ValueRangeProcessor *last_mod_range_processor; }; diff --git a/lib/database.cc b/lib/database.cc index ebe019fb..96300008 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -20,6 +20,7 @@ #include "database-private.h" #include "parse-time-vrp.h" +#include "query-fp.h" #include "string-util.h" #include @@ -1005,6 +1006,8 @@ notmuch_database_open_verbose (const char *path, * with a .. to the range processor */ notmuch->date_field_processor = new DateFieldProcessor(); notmuch->query_parser->add_boolean_prefix("date", notmuch->date_field_processor); + notmuch->query_field_processor = new QueryFieldProcessor (*notmuch->query_parser, notmuch); + notmuch->query_parser->add_boolean_prefix("query", notmuch->query_field_processor); #endif notmuch->last_mod_range_processor = new Xapian::NumberValueRangeProcessor (NOTMUCH_VALUE_LAST_MOD, "lastmod:"); diff --git a/lib/query-fp.cc b/lib/query-fp.cc new file mode 100644 index 00000000..4ffcb1e7 --- /dev/null +++ b/lib/query-fp.cc @@ -0,0 +1,43 @@ +/* query-fp.cc - "query:" field processor glue + * + * This file is part of notmuch. + * + * Copyright © 2016 David Bremner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/ . + * + * Author: David Bremner + */ + +#include "database-private.h" +#include "query-fp.h" +#include + +#if HAVE_XAPIAN_FIELD_PROCESSOR + +Xapian::Query +QueryFieldProcessor::operator() (const std::string & name) +{ + std::string key = "query." + name; + char *expansion; + notmuch_status_t status; + + status = notmuch_database_get_config (notmuch, key.c_str (), &expansion); + if (status) { + throw Xapian::QueryParserError ("error looking up key" + name); + } + + return parser.parse_query (expansion, NOTMUCH_QUERY_PARSER_FLAGS); +} +#endif diff --git a/lib/query-fp.h b/lib/query-fp.h new file mode 100644 index 00000000..67f8705d --- /dev/null +++ b/lib/query-fp.h @@ -0,0 +1,42 @@ +/* query-fp.h - query field processor glue + * + * This file is part of notmuch. + * + * Copyright © 2016 David Bremner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/ . + * + * Author: David Bremner + */ + +#ifndef NOTMUCH_QUERY_FP_H +#define NOTMUCH_QUERY_FP_H + +#include +#include "notmuch.h" + +#if HAVE_XAPIAN_FIELD_PROCESSOR +class QueryFieldProcessor : public Xapian::FieldProcessor { + protected: + Xapian::QueryParser &parser; + notmuch_database_t *notmuch; + + public: + QueryFieldProcessor (Xapian::QueryParser &parser_, notmuch_database_t *notmuch_) + : parser(parser_), notmuch(notmuch_) { }; + + Xapian::Query operator()(const std::string & str); +}; +#endif +#endif /* NOTMUCH_QUERY_FP_H */ diff --git a/test/T600-named-queries.sh b/test/T600-named-queries.sh index 09226208..f0ae24f1 100755 --- a/test/T600-named-queries.sh +++ b/test/T600-named-queries.sh @@ -50,4 +50,21 @@ notmuch restore < BEFORE notmuch dump | grep '^#@' > OUTPUT test_expect_equal_file QUERIES.BEFORE OUTPUT +if [ $NOTMUCH_HAVE_XAPIAN_FIELD_PROCESSOR -eq 1 ]; then + test_begin_subtest "search named query" + notmuch search query:test > OUTPUT + notmuch search $QUERYSTR > EXPECTED + test_expect_equal_file EXPECTED OUTPUT + + test_begin_subtest "search named query with other terms" + notmuch search query:test and subject:Maildir > OUTPUT + notmuch search $QUERYSTR and subject:Maildir > EXPECTED + test_expect_equal_file EXPECTED OUTPUT + + test_begin_subtest "search nested named query" + notmuch search query:test2 > OUTPUT + notmuch search $QUERYSTR2 > EXPECTED + test_expect_equal_file EXPECTED OUTPUT +fi + test_done From cf09631a45d276826255d197c1d5c913a29c79f4 Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Sat, 28 May 2016 20:45:31 +0300 Subject: [PATCH 028/179] lib: whitespace cleanup Cleaned the following whitespace in lib/* files: lib/index.cc: 1 line: trailing whitespace lib/database.cc 5 lines: 8 spaces at the beginning of line lib/notmuch-private.h: 4 lines: 8 spaces at the beginning of line lib/message.cc: 1 line: trailing whitespace lib/sha1.c: 1 line: empty lines at the end of file lib/query.cc: 2 lines: 8 spaces at the beginning of line lib/gen-version-script.sh: 1 line: trailing whitespace --- lib/database.cc | 10 +++++----- lib/gen-version-script.sh | 2 +- lib/index.cc | 2 +- lib/message.cc | 2 +- lib/notmuch-private.h | 8 ++++---- lib/query.cc | 4 ++-- lib/sha1.c | 1 - 7 files changed, 14 insertions(+), 15 deletions(-) diff --git a/lib/database.cc b/lib/database.cc index 96300008..0b604102 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -2177,8 +2177,8 @@ _notmuch_database_link_message_to_parents (notmuch_database_t *notmuch, * References header, if available. If not, fall back to the * first message ID in the In-Reply-To header. */ if (last_ref_message_id) { - _notmuch_message_add_term (message, "replyto", - last_ref_message_id); + _notmuch_message_add_term (message, "replyto", + last_ref_message_id); } else if (in_reply_to_message_id) { _notmuch_message_add_term (message, "replyto", in_reply_to_message_id); @@ -2287,15 +2287,15 @@ _consume_metadata_thread_id (void *ctx, notmuch_database_t *notmuch, if (stored_id.empty ()) { return NULL; } else { - Xapian::WritableDatabase *db; + Xapian::WritableDatabase *db; db = static_cast (notmuch->xapian_db); /* Clear the metadata for this message ID. We don't need it * anymore. */ - db->set_metadata (metadata_key, ""); + db->set_metadata (metadata_key, ""); - return talloc_strdup (ctx, stored_id.c_str ()); + return talloc_strdup (ctx, stored_id.c_str ()); } } diff --git a/lib/gen-version-script.sh b/lib/gen-version-script.sh index 84770011..0f908093 100644 --- a/lib/gen-version-script.sh +++ b/lib/gen-version-script.sh @@ -17,7 +17,7 @@ nm $* | awk '$1 ~ "^[0-9a-fA-F][0-9a-fA-F]*$" && $3 ~ "Xapian.*Error" {print $3 while read sym; do demangled=$(c++filt $sym) case $demangled in - typeinfo*) + typeinfo*) printf "\t$sym;\n" ;; *) diff --git a/lib/index.cc b/lib/index.cc index f166aefd..8aa844e4 100644 --- a/lib/index.cc +++ b/lib/index.cc @@ -144,7 +144,7 @@ filter_filter (GMimeFilter *gmime_filter, char *inbuf, size_t inlen, size_t pres {9, ' ', ' ', 10, 0}, {10, '\n', '\n', 11, 10}, {11, 'M', 'M', 12, 0}, - {12, ' ', '`', 12, 11} + {12, ' ', '`', 12, 11} }; int next; diff --git a/lib/message.cc b/lib/message.cc index 68393055..02226879 100644 --- a/lib/message.cc +++ b/lib/message.cc @@ -1444,7 +1444,7 @@ notmuch_message_maildir_flags_to_tags (notmuch_message_t *message) for (i = 0; i < ARRAY_SIZE(flag2tag); i++) { if ((strchr (combined_flags, flag2tag[i].flag) != NULL) - ^ + ^ flag2tag[i].inverse) { status = notmuch_message_add_tag (message, flag2tag[i].tag); diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 92807975..6063262a 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -158,8 +158,8 @@ typedef enum _notmuch_private_status { ((private_status >= (notmuch_private_status_t) NOTMUCH_STATUS_LAST_STATUS)\ ? \ _internal_error (format " (%s).\n", \ - ##__VA_ARGS__, \ - __location__), \ + ##__VA_ARGS__, \ + __location__), \ (notmuch_status_t) NOTMUCH_PRIVATE_STATUS_SUCCESS \ : \ (notmuch_status_t) private_status) @@ -477,11 +477,11 @@ _notmuch_mset_messages_move_to_next (notmuch_messages_t *messages); notmuch_bool_t _notmuch_doc_id_set_contains (notmuch_doc_id_set_t *doc_ids, - unsigned int doc_id); + unsigned int doc_id); void _notmuch_doc_id_set_remove (notmuch_doc_id_set_t *doc_ids, - unsigned int doc_id); + unsigned int doc_id); /* querying xapian documents by type (e.g. "mail" or "ghost"): */ notmuch_status_t diff --git a/lib/query.cc b/lib/query.cc index 0875b0e4..7235fde1 100644 --- a/lib/query.cc +++ b/lib/query.cc @@ -276,7 +276,7 @@ _notmuch_query_search_documents (notmuch_query_t *query, case NOTMUCH_SORT_MESSAGE_ID: enquire.set_sort_by_value (NOTMUCH_VALUE_MESSAGE_ID, FALSE); break; - case NOTMUCH_SORT_UNSORTED: + case NOTMUCH_SORT_UNSORTED: break; } @@ -412,7 +412,7 @@ _notmuch_doc_id_set_contains (notmuch_doc_id_set_t *doc_ids, void _notmuch_doc_id_set_remove (notmuch_doc_id_set_t *doc_ids, - unsigned int doc_id) + unsigned int doc_id) { if (doc_id < doc_ids->bound) doc_ids->bitmap[DOCIDSET_WORD(doc_id)] &= ~(1 << DOCIDSET_BIT(doc_id)); diff --git a/lib/sha1.c b/lib/sha1.c index 94060d57..8a6d4229 100644 --- a/lib/sha1.c +++ b/lib/sha1.c @@ -112,4 +112,3 @@ _notmuch_sha1_of_file (const char *filename) return result; } - From cfabfc6dab95f28eaa3d7261bca38ade32a69f79 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Thu, 2 Jun 2016 12:01:28 -0400 Subject: [PATCH 029/179] doc: include notmuch-emacs-mua in documentation ToC Without this patch, i'm seeing sphinx-build emit: [...]doc/man1/notmuch-emacs-mua.rst:: WARNING: document isn't included in any toctree --- doc/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/index.rst b/doc/index.rst index 3f0e6e65..344606d9 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -14,6 +14,7 @@ Contents: man1/notmuch-count man1/notmuch-dump notmuch-emacs + man1/notmuch-emacs-mua man5/notmuch-hooks man1/notmuch-insert man1/notmuch-new From 9b9c265a2585cc808f247340e9cb5a12de8a0da9 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Thu, 2 Jun 2016 12:26:10 -0400 Subject: [PATCH 030/179] NEWS, python: update pointer to online documentation Currently, http://packages.python.org/notmuch/ goes through a series of redirections and ends up pointing to readthedocs. Since we're using readthedocs directly anyway, just point to it directly. readthedocs are also now sensibly using a separate domain (readthedocs.io) for their hosted documentation as distinct from their own domain (readthedocs.org), so use the correct tld. --- NEWS | 2 +- bindings/python/README | 2 +- bindings/python/setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 90e233bf..cb6e21cd 100644 --- a/NEWS +++ b/NEWS @@ -2361,7 +2361,7 @@ Ruby bindings are now much more complete Python bindings have been updated and extended - (docs online at http://packages.python.org/notmuch/) + (docs online at https://notmuch.readthedocs.io/) New bindings: diff --git a/bindings/python/README b/bindings/python/README index b20ae071..695b5bd2 100644 --- a/bindings/python/README +++ b/bindings/python/README @@ -10,7 +10,7 @@ If you have downloaded the full source tarball, you can create the documentation with sphinx installed, go to the docs directory and "make html". A static version of the documentation is available at: - https://notmuch.readthedocs.org/projects/notmuch-python/ + https://notmuch.readthedocs.io/projects/notmuch-python/ To build the python bindings, do diff --git a/bindings/python/setup.py b/bindings/python/setup.py index f4c338e3..ba95d951 100644 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -48,7 +48,7 @@ for extremely quick searching and filtering of your email according to various criteria. The documentation for the latest notmuch release can be `viewed -online `_. +online `_. Requirements ------------ From 8540c36a96f5270d84d19dcbfbc9f6463f1653fd Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Thu, 2 Jun 2016 12:26:11 -0400 Subject: [PATCH 031/179] completion: update the location of the bash-completion project Alioth currently redirects to github, so point there instead. --- completion/README | 2 +- completion/notmuch-completion.bash | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/completion/README b/completion/README index e200c189..89805c72 100644 --- a/completion/README +++ b/completion/README @@ -9,7 +9,7 @@ notmuch-completion.bash bash-completion package [1] version 2.0, which depends on bash version 3.2 or later. - [1] http://bash-completion.alioth.debian.org/ + [1] https://github.com/scop/bash-completion notmuch-completion.zsh diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index cc583924..29cdf431 100644 --- a/completion/notmuch-completion.bash +++ b/completion/notmuch-completion.bash @@ -3,7 +3,7 @@ # Copyright © 2013 Jani Nikula # # Based on the bash-completion package: -# http://bash-completion.alioth.debian.org/ +# https://github.com/scop/bash-completion # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From 385f62baa1df439cb6365101faf31c0f6f2f2935 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Thu, 2 Jun 2016 12:26:12 -0400 Subject: [PATCH 032/179] update link to Chip Rosenthal article about reply-to munging The original link is currently returning an internal server error :( --- notmuch-reply.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index a74194a3..457e54ab 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -324,7 +324,7 @@ add_recipients_from_message (GMimeMessage *reply, unsigned int n = 0; /* Some mailing lists munge the Reply-To header despite it being A Bad - * Thing, see http://www.unicom.com/pw/reply-to-harmful.html + * Thing, see http://marc.merlins.org/netrants/reply-to-harmful.html * * The munging is easy to detect, because it results in a * redundant reply-to header, (with an address that already exists From 554b90b58e367ada5fdad3165c76673458f65434 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Thu, 2 Jun 2016 12:26:13 -0400 Subject: [PATCH 033/179] nmbug: updated to modern URL for PEP 343 --- devel/nmbug/nmbug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devel/nmbug/nmbug b/devel/nmbug/nmbug index 0787b2ba..e406395d 100755 --- a/devel/nmbug/nmbug +++ b/devel/nmbug/nmbug @@ -80,7 +80,7 @@ except AttributeError: # Python < 3.2 See PEP 343 for details on context managers [1]. - [1]: http://legacy.python.org/dev/peps/pep-0343/ + [1]: https://www.python.org/dev/peps/pep-0343/ """ def __init__(self, **kwargs): self.name = _tempfile.mkdtemp(**kwargs) From 6a833a6e83865f6999707cc30768d07e1351c2cb Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Thu, 2 Jun 2016 12:26:14 -0400 Subject: [PATCH 034/179] Use https instead of http where possible Many of the external links found in the notmuch source can be resolved using https instead of http. This changeset addresses as many as i could find, without touching the e-mail corpus or expected outputs found in tests. --- COPYING | 2 +- COPYING-GPL-3 | 8 ++++---- INSTALL | 4 ++-- Makefile.local | 4 ++-- README | 2 +- README.rst | 6 +++--- bindings/go/README | 2 +- bindings/go/src/notmuch/notmuch.go | 2 +- bindings/python/README | 2 +- bindings/python/docs/COPYING | 6 +++--- bindings/python/docs/source/index.rst | 2 +- bindings/python/notmuch/__init__.py | 2 +- bindings/python/notmuch/compat.py | 2 +- bindings/python/notmuch/database.py | 2 +- bindings/python/notmuch/directory.py | 2 +- bindings/python/notmuch/errors.py | 2 +- bindings/python/notmuch/filenames.py | 2 +- bindings/python/notmuch/globals.py | 2 +- bindings/python/notmuch/message.py | 2 +- bindings/python/notmuch/messages.py | 2 +- bindings/python/notmuch/query.py | 2 +- bindings/python/notmuch/tag.py | 2 +- bindings/python/notmuch/thread.py | 2 +- bindings/python/notmuch/threads.py | 2 +- bindings/python/setup.py | 10 +++++----- bindings/ruby/database.c | 2 +- bindings/ruby/defs.h | 2 +- bindings/ruby/directory.c | 2 +- bindings/ruby/filenames.c | 2 +- bindings/ruby/init.c | 2 +- bindings/ruby/message.c | 2 +- bindings/ruby/messages.c | 2 +- bindings/ruby/query.c | 2 +- bindings/ruby/status.c | 2 +- bindings/ruby/tags.c | 2 +- bindings/ruby/thread.c | 2 +- bindings/ruby/threads.c | 2 +- compat/compat.h | 2 +- compat/function-attributes.h | 2 +- completion/notmuch-completion.bash | 2 +- configure | 4 ++-- contrib/notmuch-mutt/README | 6 +++--- contrib/notmuch-mutt/notmuch-mutt | 2 +- contrib/notmuch-vim/plugin/notmuch.vim | 4 ++-- crypto.c | 2 +- debian/control | 4 ++-- debugger.c | 2 +- devel/man-to-mdwn.pl | 2 +- devel/nmbug/doc/man5/notmuch-report.json.5.rst | 2 +- devel/nmbug/nmbug | 8 ++++---- devel/nmbug/notmuch-report | 2 +- devel/nmbug/notmuch-report.json | 2 +- devel/try-emacs-mua | 2 +- doc/doxygen.cfg | 2 +- doc/man1/notmuch.rst | 4 ++-- emacs/coolj.el | 2 +- emacs/make-deps.el | 2 +- emacs/notmuch-address.el | 2 +- emacs/notmuch-company.el | 2 +- emacs/notmuch-crypto.el | 2 +- emacs/notmuch-hello.el | 4 ++-- emacs/notmuch-jump.el | 2 +- emacs/notmuch-lib.el | 2 +- emacs/notmuch-message.el | 2 +- emacs/notmuch-mua.el | 6 +++--- emacs/notmuch-parser.el | 2 +- emacs/notmuch-print.el | 2 +- emacs/notmuch-query.el | 2 +- emacs/notmuch-show.el | 8 ++++---- emacs/notmuch-tag.el | 2 +- emacs/notmuch-tree.el | 2 +- emacs/notmuch-version.el.tmpl | 2 +- emacs/notmuch-wash.el | 2 +- emacs/notmuch.el | 6 +++--- hooks.c | 2 +- lib/built-with.c | 2 +- lib/config.cc | 2 +- lib/database-private.h | 2 +- lib/database.cc | 4 ++-- lib/directory.cc | 2 +- lib/filenames.c | 2 +- lib/gen-version-script.sh | 2 +- lib/index.cc | 2 +- lib/message-file.c | 2 +- lib/message.cc | 2 +- lib/messages.c | 2 +- lib/notmuch-private.h | 2 +- lib/notmuch.h | 4 ++-- lib/parse-time-vrp.cc | 2 +- lib/parse-time-vrp.h | 2 +- lib/query-fp.cc | 2 +- lib/query-fp.h | 2 +- lib/query.cc | 2 +- lib/sha1.c | 2 +- lib/string-list.c | 2 +- lib/tags.c | 2 +- lib/thread.cc | 2 +- mime-node.c | 2 +- notmuch-client.h | 2 +- notmuch-compact.c | 2 +- notmuch-config.c | 4 ++-- notmuch-count.c | 2 +- notmuch-dump.c | 2 +- notmuch-emacs-mua | 2 +- notmuch-insert.c | 2 +- notmuch-new.c | 2 +- notmuch-reply.c | 2 +- notmuch-restore.c | 2 +- notmuch-search.c | 2 +- notmuch-setup.c | 2 +- notmuch-show.c | 2 +- notmuch-tag.c | 2 +- notmuch-time.c | 2 +- notmuch.c | 4 ++-- packaging/fedora/notmuch.spec | 4 ++-- parse-time-string/parse-time-string.c | 2 +- parse-time-string/parse-time-string.h | 2 +- performance-test/Makefile.local | 4 ++-- performance-test/README | 2 +- query-string.c | 2 +- sprinter-sexp.c | 2 +- test/T310-emacs.sh | 4 ++-- test/T360-symbol-hiding.sh | 2 +- test/database-test.c | 2 +- test/parse-time.c | 2 +- test/random-corpus.c | 2 +- test/smtp-dummy.c | 2 +- test/test-databases/Makefile.local | 2 +- test/test-lib-common.sh | 2 +- test/test-lib.el | 2 +- test/test-lib.sh | 2 +- util/error_util.c | 2 +- util/error_util.h | 2 +- util/hex-escape.c | 2 +- util/string-util.c | 2 +- util/xutil.c | 2 +- util/xutil.h | 2 +- util/zlib-extra.c | 2 +- 138 files changed, 175 insertions(+), 175 deletions(-) diff --git a/COPYING b/COPYING index 6dea6939..4e744d26 100644 --- a/COPYING +++ b/COPYING @@ -12,4 +12,4 @@ General Public License for more details. You should have received a copy of the GNU General Public License along with this program, (in the COPYING-GPL-3 file in this -directory). If not, see http://www.gnu.org/licenses/ +directory). If not, see https://www.gnu.org/licenses/ diff --git a/COPYING-GPL-3 b/COPYING-GPL-3 index 44325404..4c493545 100644 --- a/COPYING-GPL-3 +++ b/COPYING-GPL-3 @@ -2,7 +2,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -646,7 +646,7 @@ the "copyright" line and a pointer to where the full notice is found. GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -665,12 +665,12 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. diff --git a/INSTALL b/INSTALL index b1b9cd55..6099ed01 100644 --- a/INSTALL +++ b/INSTALL @@ -30,7 +30,7 @@ Talloc, and zlib which are each described below: It provides all the real machinery of indexing and searching, (including the very nice parsing of the query string). - Xapian is available from http://xapian.org + Xapian is available from https://xapian.org Note: Notmuch will work best with Xapian 1.0.18 (or later) or Xapian 1.1.4 (or later). Previous versions of Xapian (whether @@ -58,7 +58,7 @@ Talloc, and zlib which are each described below: made development of Notmuch much easier and much less prone to memory leaks. - Talloc is available from http://talloc.samba.org/ + Talloc is available from https://talloc.samba.org/ zlib ---- diff --git a/Makefile.local b/Makefile.local index 066ecf23..cf16185e 100644 --- a/Makefile.local +++ b/Makefile.local @@ -35,7 +35,7 @@ DEB_TAG=debian/$(UPSTREAM_TAG)-1 RELEASE_HOST=notmuchmail.org RELEASE_DIR=/srv/notmuchmail.org/www/releases -RELEASE_URL=http://notmuchmail.org/releases +RELEASE_URL=https://notmuchmail.org/releases TAR_FILE=$(PACKAGE)-$(VERSION).tar.gz DEB_TAR_FILE=$(PACKAGE)_$(VERSION).orig.tar.gz SHA1_FILE=$(TAR_FILE).sha1 @@ -191,7 +191,7 @@ release-message: @echo "the Xapian library to provide fast, full-text search with a convenient" @echo "search syntax." @echo "" - @echo "For more about notmuch, see http://notmuchmail.org" + @echo "For more about notmuch, see https://notmuchmail.org" # This is a chain of dependencies rather than a simple list simply to # avoid the messages getting interleaved in the case of a parallel diff --git a/README b/README index d92fcfdf..0aa9a080 100644 --- a/README +++ b/README @@ -58,7 +58,7 @@ Contacting users and developers ------------------------------- The website for Notmuch is: - http://notmuchmail.org + https://notmuchmail.org The mailing list address for the notmuch community is: diff --git a/README.rst b/README.rst index 7417ddcb..7ff3198c 100644 --- a/README.rst +++ b/README.rst @@ -1,11 +1,11 @@ -If you're reading this on http://github.com/notmuch/notmuch, this is a +If you're reading this on https://github.com/notmuch/notmuch, this is a read-only mirror of the notmuch project. -For more information about the project, see http://notmuchmail.org. +For more information about the project, see https://notmuchmail.org. Please don't send us pull requests on github. If you have a feature branch that you want us to look at, use ``git send-email`` to send it to notmuch@notmuchmail.org. For more information about contributing to the project, see -http://notmuchmail.org/contributing/. +https://notmuchmail.org/contributing/. diff --git a/bindings/go/README b/bindings/go/README index 18a54b60..1825ae03 100644 --- a/bindings/go/README +++ b/bindings/go/README @@ -13,4 +13,4 @@ todo - improve notmuch-addrlookup regexp -[1] http://notmuchmail.org/ \ No newline at end of file +[1] https://notmuchmail.org/ diff --git a/bindings/go/src/notmuch/notmuch.go b/bindings/go/src/notmuch/notmuch.go index 0fff1ab6..4d8c2ceb 100644 --- a/bindings/go/src/notmuch/notmuch.go +++ b/bindings/go/src/notmuch/notmuch.go @@ -351,7 +351,7 @@ func (self *Database) GetAllTags() *Tags { * completely in the future, but it's likely to be a specialized * version of the general Xapian query syntax: * - * http://xapian.org/docs/queryparser.html + * https://xapian.org/docs/queryparser.html * * As a special case, passing either a length-zero string, (that is ""), * or a string consisting of a single asterisk (that is "*"), will diff --git a/bindings/python/README b/bindings/python/README index 695b5bd2..fe7a2181 100644 --- a/bindings/python/README +++ b/bindings/python/README @@ -2,7 +2,7 @@ notmuch -- The python interface to notmuch ========================================== This module makes the functionality of the notmuch library -(`http://notmuchmail.org`_) available to python. Successful import of +(`https://notmuchmail.org`_) available to python. Successful import of this modul depends on a libnotmuch.so|dll being available on the user's system. diff --git a/bindings/python/docs/COPYING b/bindings/python/docs/COPYING index 94a9ed02..2a000655 100644 --- a/bindings/python/docs/COPYING +++ b/bindings/python/docs/COPYING @@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found. GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. diff --git a/bindings/python/docs/source/index.rst b/bindings/python/docs/source/index.rst index 1cece5f7..bef7e60d 100644 --- a/bindings/python/docs/source/index.rst +++ b/bindings/python/docs/source/index.rst @@ -4,7 +4,7 @@ Welcome to :mod:`notmuch`'s documentation .. currentmodule:: notmuch The :mod:`notmuch` module provides an interface to the `notmuch -`_ functionality, directly interfacing to a +`_ functionality, directly interfacing to a shared notmuch library. Within :mod:`notmuch`, the classes :class:`Database`, :class:`Query` provide most of the core functionality, returning :class:`Threads`, :class:`Messages` and diff --git a/bindings/python/notmuch/__init__.py b/bindings/python/notmuch/__init__.py index 29416a5b..cf627ffa 100644 --- a/bindings/python/notmuch/__init__.py +++ b/bindings/python/notmuch/__init__.py @@ -47,7 +47,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with notmuch. If not, see . +along with notmuch. If not, see . Copyright 2010-2011 Sebastian Spaeth """ diff --git a/bindings/python/notmuch/compat.py b/bindings/python/notmuch/compat.py index daa268c1..c931329e 100644 --- a/bindings/python/notmuch/compat.py +++ b/bindings/python/notmuch/compat.py @@ -16,7 +16,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with notmuch. If not, see . +along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth Copyright 2012 Justus Winter <4winter@informatik.uni-hamburg.de> diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index f3045334..67fb1c41 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -12,7 +12,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with notmuch. If not, see . +along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth """ diff --git a/bindings/python/notmuch/directory.py b/bindings/python/notmuch/directory.py index 3b0a525d..7f86b1ac 100644 --- a/bindings/python/notmuch/directory.py +++ b/bindings/python/notmuch/directory.py @@ -12,7 +12,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with notmuch. If not, see . +along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth """ diff --git a/bindings/python/notmuch/errors.py b/bindings/python/notmuch/errors.py index abca51d7..b7684ef6 100644 --- a/bindings/python/notmuch/errors.py +++ b/bindings/python/notmuch/errors.py @@ -12,7 +12,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with notmuch. If not, see . +along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth """ diff --git a/bindings/python/notmuch/filenames.py b/bindings/python/notmuch/filenames.py index f8f383e4..29f4fdf6 100644 --- a/bindings/python/notmuch/filenames.py +++ b/bindings/python/notmuch/filenames.py @@ -12,7 +12,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with notmuch. If not, see . +along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth """ diff --git a/bindings/python/notmuch/globals.py b/bindings/python/notmuch/globals.py index 6872a291..b1eec2cf 100644 --- a/bindings/python/notmuch/globals.py +++ b/bindings/python/notmuch/globals.py @@ -12,7 +12,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with notmuch. If not, see . +along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth """ diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py index d1c1b58c..bf809008 100644 --- a/bindings/python/notmuch/message.py +++ b/bindings/python/notmuch/message.py @@ -12,7 +12,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with notmuch. If not, see . +along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth Jesse Rosenthal diff --git a/bindings/python/notmuch/messages.py b/bindings/python/notmuch/messages.py index 76100ffb..e17f1507 100644 --- a/bindings/python/notmuch/messages.py +++ b/bindings/python/notmuch/messages.py @@ -12,7 +12,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with notmuch. If not, see . +along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth Jesse Rosenthal diff --git a/bindings/python/notmuch/query.py b/bindings/python/notmuch/query.py index 43270072..a0f4f64b 100644 --- a/bindings/python/notmuch/query.py +++ b/bindings/python/notmuch/query.py @@ -12,7 +12,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with notmuch. If not, see . +along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth """ diff --git a/bindings/python/notmuch/tag.py b/bindings/python/notmuch/tag.py index 1d523457..3b4a56ac 100644 --- a/bindings/python/notmuch/tag.py +++ b/bindings/python/notmuch/tag.py @@ -12,7 +12,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with notmuch. If not, see . +along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth """ diff --git a/bindings/python/notmuch/thread.py b/bindings/python/notmuch/thread.py index 0454dbd4..cc151f7e 100644 --- a/bindings/python/notmuch/thread.py +++ b/bindings/python/notmuch/thread.py @@ -12,7 +12,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with notmuch. If not, see . +along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth """ diff --git a/bindings/python/notmuch/threads.py b/bindings/python/notmuch/threads.py index a550523f..86f1f2cc 100644 --- a/bindings/python/notmuch/threads.py +++ b/bindings/python/notmuch/threads.py @@ -12,7 +12,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with notmuch. If not, see . +along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth """ diff --git a/bindings/python/setup.py b/bindings/python/setup.py index ba95d951..d986f0c6 100644 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -14,7 +14,7 @@ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with notmuch. If not, see . +along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth """ @@ -34,15 +34,15 @@ setup(name='notmuch', description='Python binding of the notmuch mail search and indexing library.', author='Sebastian Spaeth', author_email='Sebastian@SSpaeth.de', - url='http://notmuchmail.org/', - download_url='http://notmuchmail.org/releases/notmuch-%s.tar.gz' % __VERSION__, + url='https://notmuchmail.org/', + download_url='https://notmuchmail.org/releases/notmuch-%s.tar.gz' % __VERSION__, packages=['notmuch'], keywords=['library', 'email'], long_description='''Overview ======== The notmuch module provides an interface to the `notmuch -`_ functionality, directly interfacing with a +`_ functionality, directly interfacing with a shared notmuch library. Notmuch provides a maildatabase that allows for extremely quick searching and filtering of your email according to various criteria. @@ -66,5 +66,5 @@ python >= 2.5. It will not work on earlier python versions. 'Topic :: Software Development :: Libraries' ], platforms='', - license='http://www.gnu.org/licenses/gpl-3.0.txt', + license='https://www.gnu.org/licenses/gpl-3.0.txt', ) diff --git a/bindings/ruby/database.c b/bindings/ruby/database.c index 6deda575..12e6bab7 100644 --- a/bindings/ruby/database.c +++ b/bindings/ruby/database.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Ali Polatel */ diff --git a/bindings/ruby/defs.h b/bindings/ruby/defs.h index 167250e4..48544ca2 100644 --- a/bindings/ruby/defs.h +++ b/bindings/ruby/defs.h @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Ali Polatel */ diff --git a/bindings/ruby/directory.c b/bindings/ruby/directory.c index 303523c2..0f37b391 100644 --- a/bindings/ruby/directory.c +++ b/bindings/ruby/directory.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Ali Polatel */ diff --git a/bindings/ruby/filenames.c b/bindings/ruby/filenames.c index e2785903..656c58e6 100644 --- a/bindings/ruby/filenames.c +++ b/bindings/ruby/filenames.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Ali Polatel */ diff --git a/bindings/ruby/init.c b/bindings/ruby/init.c index 865d6bf6..5556b43e 100644 --- a/bindings/ruby/init.c +++ b/bindings/ruby/init.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Ali Polatel */ diff --git a/bindings/ruby/message.c b/bindings/ruby/message.c index 4ff6097f..c55cf6e2 100644 --- a/bindings/ruby/message.c +++ b/bindings/ruby/message.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Ali Polatel */ diff --git a/bindings/ruby/messages.c b/bindings/ruby/messages.c index 443a30c9..a337feeb 100644 --- a/bindings/ruby/messages.c +++ b/bindings/ruby/messages.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Ali Polatel */ diff --git a/bindings/ruby/query.c b/bindings/ruby/query.c index 8cbc73f2..ce66926c 100644 --- a/bindings/ruby/query.c +++ b/bindings/ruby/query.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Ali Polatel */ diff --git a/bindings/ruby/status.c b/bindings/ruby/status.c index b11fb9fb..a0f88633 100644 --- a/bindings/ruby/status.c +++ b/bindings/ruby/status.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Ali Polatel */ diff --git a/bindings/ruby/tags.c b/bindings/ruby/tags.c index e8226ad7..db8b4cfc 100644 --- a/bindings/ruby/tags.c +++ b/bindings/ruby/tags.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Ali Polatel */ diff --git a/bindings/ruby/thread.c b/bindings/ruby/thread.c index 56616d9f..9b295981 100644 --- a/bindings/ruby/thread.c +++ b/bindings/ruby/thread.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Ali Polatel */ diff --git a/bindings/ruby/threads.c b/bindings/ruby/threads.c index 3e1fbf5d..ed403a8f 100644 --- a/bindings/ruby/threads.c +++ b/bindings/ruby/threads.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Ali Polatel */ diff --git a/compat/compat.h b/compat/compat.h index 634d505b..88bc4df4 100644 --- a/compat/compat.h +++ b/compat/compat.h @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/compat/function-attributes.h b/compat/function-attributes.h index 8450a17d..1945b5bf 100644 --- a/compat/function-attributes.h +++ b/compat/function-attributes.h @@ -14,7 +14,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . */ #ifndef FUNCTION_ATTRIBUTES_H diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index 29cdf431..78047b5f 100644 --- a/completion/notmuch-completion.bash +++ b/completion/notmuch-completion.bash @@ -16,7 +16,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see http://www.gnu.org/licenses/ . +# along with this program. If not, see https://www.gnu.org/licenses/ . # # Author: Jani Nikula # diff --git a/configure b/configure index ce1d6983..49fa5b93 100755 --- a/configure +++ b/configure @@ -643,7 +643,7 @@ EOF fi if [ $have_xapian -eq 0 ]; then echo " Xapian library (including development files such as headers)" - echo " http://xapian.org/" + echo " https://xapian.org/" fi if [ $have_zlib -eq 0 ]; then echo " zlib library (>= version 1.2.5.2, including development files such as headers)" @@ -663,7 +663,7 @@ EOF fi if [ $have_talloc -eq 0 ]; then echo " The talloc library (including development files such as headers)" - echo " http://talloc.samba.org/" + echo " https://talloc.samba.org/" echo fi cat <. notmuch-mutt is released under the terms of the GNU General Public License (GPL), version 3 or above. A copy of the license is available online at -. +. diff --git a/contrib/notmuch-mutt/notmuch-mutt b/contrib/notmuch-mutt/notmuch-mutt index 97fb2d85..0e46a8c1 100755 --- a/contrib/notmuch-mutt/notmuch-mutt +++ b/contrib/notmuch-mutt/notmuch-mutt @@ -91,7 +91,7 @@ sub get_message_id() { $mid = $1; } else { # Message-ID header not found, synthesize a message id # based on SHA1, as notmuch would do. See: - # http://git.notmuchmail.org/git/notmuch/blob/HEAD:/lib/sha1.c + # https://git.notmuchmail.org/git/notmuch/blob/HEAD:/lib/sha1.c my $sha = Digest::SHA->new(1); $sha->add($_) foreach(@headers); $sha->addfile(\*STDIN); diff --git a/contrib/notmuch-vim/plugin/notmuch.vim b/contrib/notmuch-vim/plugin/notmuch.vim index 8f27fb92..eaa984bc 100644 --- a/contrib/notmuch-vim/plugin/notmuch.vim +++ b/contrib/notmuch-vim/plugin/notmuch.vim @@ -15,7 +15,7 @@ " General Public License for more details. " " You should have received a copy of the GNU General Public License -" along with Notmuch. If not, see . +" along with Notmuch. If not, see . " " Authors: Bart Trojanowski " Contributors: Felipe Contreras , @@ -1198,7 +1198,7 @@ function! s:NM_shell_escape(word) endfunction " this function was taken from git.vim, then fixed up -" http://github.com/motemen/git-vim +" https://github.com/motemen/git-vim function! s:NM_shell_split(cmd) let l:split_cmd = [] let cmd = a:cmd diff --git a/crypto.c b/crypto.c index 3dabc97b..3e8ce7ca 100644 --- a/crypto.c +++ b/crypto.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Authors: Jameson Rollins */ diff --git a/debian/control b/debian/control index a46e391b..d846119b 100644 --- a/debian/control +++ b/debian/control @@ -26,9 +26,9 @@ Build-Depends: gpgsm , bash-completion (>=1.9.0~) Standards-Version: 3.9.6 -Homepage: http://notmuchmail.org/ +Homepage: https://notmuchmail.org/ Vcs-Git: git://notmuchmail.org/git/notmuch -Vcs-Browser: http://git.notmuchmail.org/git/notmuch +Vcs-Browser: https://git.notmuchmail.org/git/notmuch Package: notmuch Architecture: any diff --git a/debugger.c b/debugger.c index e8b9378e..0fa0fb6b 100644 --- a/debugger.c +++ b/debugger.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Chris Wilson */ diff --git a/devel/man-to-mdwn.pl b/devel/man-to-mdwn.pl index ea284d08..f9d31b73 100755 --- a/devel/man-to-mdwn.pl +++ b/devel/man-to-mdwn.pl @@ -200,6 +200,6 @@ foreach (sort srt values %fhash) print <<'EOF'; The manual pages are licensed under -[the GNU General Public License](http://www.gnu.org/licenses/gpl.txt), +[the GNU General Public License](https://www.gnu.org/licenses/gpl.txt), either version 3.0 or at your option any later version. EOF diff --git a/devel/nmbug/doc/man5/notmuch-report.json.5.rst b/devel/nmbug/doc/man5/notmuch-report.json.5.rst index 4b5f84a8..1207a4aa 100644 --- a/devel/nmbug/doc/man5/notmuch-report.json.5.rst +++ b/devel/nmbug/doc/man5/notmuch-report.json.5.rst @@ -92,7 +92,7 @@ EXAMPLE { "meta": { "title": "Notmuch Patches", - "blurb": "For more information see nmbug", + "blurb": "For more information see nmbug", "header": "

{title}

{blurb}

Views

", "footer": "

Generated: {datetime}

", "message-url": "http://mid.gmane.org/{message-id}" diff --git a/devel/nmbug/nmbug b/devel/nmbug/nmbug index e406395d..1dd5f14f 100755 --- a/devel/nmbug/nmbug +++ b/devel/nmbug/nmbug @@ -14,7 +14,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see http://www.gnu.org/licenses/ . +# along with this program. If not, see https://www.gnu.org/licenses/ . """ Manage notmuch tags with Git @@ -119,9 +119,9 @@ def _xapian_quote(string): Xapian uses double-quotes for quoting strings. You can escape internal quotes by repeating them [1,2,3]. - [1]: http://trac.xapian.org/ticket/128#comment:2 - [2]: http://trac.xapian.org/ticket/128#comment:17 - [3]: http://trac.xapian.org/changeset/13823/svn + [1]: https://trac.xapian.org/ticket/128#comment:2 + [2]: https://trac.xapian.org/ticket/128#comment:17 + [3]: https://trac.xapian.org/changeset/13823/svn """ return '"{0}"'.format(string.replace('"', '""')) diff --git a/devel/nmbug/notmuch-report b/devel/nmbug/notmuch-report index 87390c1e..a9c2a6ec 100755 --- a/devel/nmbug/notmuch-report +++ b/devel/nmbug/notmuch-report @@ -17,7 +17,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see http://www.gnu.org/licenses/ . +# along with this program. If not, see https://www.gnu.org/licenses/ . """Generate text and/or HTML for one or more notmuch searches. diff --git a/devel/nmbug/notmuch-report.json b/devel/nmbug/notmuch-report.json index 48b6f19f..c5b35b17 100644 --- a/devel/nmbug/notmuch-report.json +++ b/devel/nmbug/notmuch-report.json @@ -1,7 +1,7 @@ { "meta": { "title": "Notmuch Patches", - "blurb": "For more information see nmbug" + "blurb": "For more information see nmbug" }, "views": [ diff --git a/devel/try-emacs-mua b/devel/try-emacs-mua index b0a62c25..041f6216 100755 --- a/devel/try-emacs-mua +++ b/devel/try-emacs-mua @@ -7,7 +7,7 @@ ;; ;; Authors: Tomi Ollila ;; -;; http://www.emacswiki.org/emacs/EmacsScripts was a useful starting point... +;; https://www.emacswiki.org/emacs/EmacsScripts was a useful starting point... ;; ;; Licence: GPLv3+ ;; diff --git a/doc/doxygen.cfg b/doc/doxygen.cfg index c033f344..2ca15d41 100644 --- a/doc/doxygen.cfg +++ b/doc/doxygen.cfg @@ -177,7 +177,7 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES USE_MATHJAX = NO MATHJAX_FORMAT = HTML-CSS -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_RELPATH = https://cdn.mathjax.org/mathjax/latest MATHJAX_EXTENSIONS = MATHJAX_CODEFILE = SEARCHENGINE = YES diff --git a/doc/man1/notmuch.rst b/doc/man1/notmuch.rst index 3acfbdb4..edd04efb 100644 --- a/doc/man1/notmuch.rst +++ b/doc/man1/notmuch.rst @@ -29,7 +29,7 @@ While the command-line program ``notmuch`` provides powerful functionality, it does not provide the most convenient interface for that functionality. More sophisticated interfaces are expected to be built on top of either the command-line interface, or more likely, on -top of the notmuch library interface. See http://notmuchmail.org for +top of the notmuch library interface. See https://notmuchmail.org for more about alternate interfaces to notmuch. The emacs-based interface to notmuch (available under **emacs/** in the Notmuch source distribution) is probably the most widely used at this time. @@ -144,7 +144,7 @@ SEE ALSO **notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**, **notmuch-address(1)** -The notmuch website: **http://notmuchmail.org** +The notmuch website: **https://notmuchmail.org** CONTACT ======= diff --git a/emacs/coolj.el b/emacs/coolj.el index 77550602..350d537f 100644 --- a/emacs/coolj.el +++ b/emacs/coolj.el @@ -21,7 +21,7 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs. If not, see . +;; along with GNU Emacs. If not, see . ;;; Commentary: diff --git a/emacs/make-deps.el b/emacs/make-deps.el index 24c1a457..5b6db698 100644 --- a/emacs/make-deps.el +++ b/emacs/make-deps.el @@ -15,7 +15,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;; ;; Authors: Austin Clements diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el index 8b79d340..d372fbaf 100644 --- a/emacs/notmuch-address.el +++ b/emacs/notmuch-address.el @@ -15,7 +15,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;; ;; Authors: David Edmondson diff --git a/emacs/notmuch-company.el b/emacs/notmuch-company.el index b881d6dc..0619c7ef 100644 --- a/emacs/notmuch-company.el +++ b/emacs/notmuch-company.el @@ -16,7 +16,7 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . +;; along with this program. If not, see . ;;; Commentary: diff --git a/emacs/notmuch-crypto.el b/emacs/notmuch-crypto.el index 004463c3..e376aa80 100644 --- a/emacs/notmuch-crypto.el +++ b/emacs/notmuch-crypto.el @@ -15,7 +15,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;; ;; Authors: Jameson Rollins diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el index 9495c1a4..75ccf579 100644 --- a/emacs/notmuch-hello.el +++ b/emacs/notmuch-hello.el @@ -15,7 +15,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;; ;; Authors: David Edmondson @@ -265,7 +265,7 @@ International Bureau of Weights and Measures." :group 'notmuch-hello :group 'notmuch-hooks) -(defvar notmuch-hello-url "http://notmuchmail.org" +(defvar notmuch-hello-url "https://notmuchmail.org" "The `notmuch' web site.") (defvar notmuch-hello-custom-section-options diff --git a/emacs/notmuch-jump.el b/emacs/notmuch-jump.el index fd770f1e..963253c9 100644 --- a/emacs/notmuch-jump.el +++ b/emacs/notmuch-jump.el @@ -15,7 +15,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;; ;; Authors: Austin Clements ;; David Edmondson diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index f05ded6f..1781af48 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -15,7 +15,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;; ;; Authors: Carl Worth diff --git a/emacs/notmuch-message.el b/emacs/notmuch-message.el index d437b857..55e4cfee 100644 --- a/emacs/notmuch-message.el +++ b/emacs/notmuch-message.el @@ -15,7 +15,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;; ;; Authors: Jesse Rosenthal diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index 399e1380..1ca80564 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -15,7 +15,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;; ;; Authors: David Edmondson @@ -142,7 +142,7 @@ mutiple parts get a header." (let ((notmuch-version (if (string= notmuch-emacs-version "unknown") (notmuch-cli-version) notmuch-emacs-version))) - (concat "Notmuch/" notmuch-version " (http://notmuchmail.org)"))) + (concat "Notmuch/" notmuch-version " (https://notmuchmail.org)"))) (defun notmuch-mua-user-agent-emacs () "Generate a `User-Agent:' string suitable for notmuch." @@ -334,7 +334,7 @@ modified. This function is notmuch addaptation of ;; C-h f compose-mail says that headers should be specified as ;; (string . value); however all the rest of message expects ;; headers to be symbols, not strings (eg message-header-format-alist). - ;; http://lists.gnu.org/archive/html/emacs-devel/2011-01/msg00337.html + ;; https://lists.gnu.org/archive/html/emacs-devel/2011-01/msg00337.html ;; We need to convert any string input, eg from rmail-start-mail. (dolist (h other-headers other-headers) (if (stringp (car h)) (setcar h (intern (capitalize (car h)))))))) diff --git a/emacs/notmuch-parser.el b/emacs/notmuch-parser.el index 620ca89d..bb0379c1 100644 --- a/emacs/notmuch-parser.el +++ b/emacs/notmuch-parser.el @@ -15,7 +15,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;; ;; Authors: Austin Clements diff --git a/emacs/notmuch-print.el b/emacs/notmuch-print.el index 480a0cfe..bca759fa 100644 --- a/emacs/notmuch-print.el +++ b/emacs/notmuch-print.el @@ -15,7 +15,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;; ;; Authors: David Edmondson diff --git a/emacs/notmuch-query.el b/emacs/notmuch-query.el index 8587d881..436ad160 100644 --- a/emacs/notmuch-query.el +++ b/emacs/notmuch-query.el @@ -15,7 +15,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;; ;; Authors: David Bremner diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index f33096cf..fea39faf 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -16,7 +16,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;; ;; Authors: Carl Worth ;; David Edmondson @@ -182,9 +182,9 @@ each attachment handler is logged in buffers with names beginning (defcustom notmuch-show-stash-mlarchive-link-alist '(("Gmane" . "http://mid.gmane.org/") - ("MARC" . "http://marc.info/?i=") - ("Mail Archive, The" . "http://mid.mail-archive.com/") - ("LKML" . "http://lkml.kernel.org/r/") + ("MARC" . "https://marc.info/?i=") + ("Mail Archive, The" . "https://mid.mail-archive.com/") + ("LKML" . "https://lkml.kernel.org/r/") ;; FIXME: can these services be searched by `Message-Id' ? ;; ("MarkMail" . "http://markmail.org/") ;; ("Nabble" . "http://nabble.com/") diff --git a/emacs/notmuch-tag.el b/emacs/notmuch-tag.el index 98064a3b..a3f0c52a 100644 --- a/emacs/notmuch-tag.el +++ b/emacs/notmuch-tag.el @@ -16,7 +16,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;; ;; Authors: Carl Worth ;; Damien Cassou diff --git a/emacs/notmuch-tree.el b/emacs/notmuch-tree.el index 4f9ca2de..52313199 100644 --- a/emacs/notmuch-tree.el +++ b/emacs/notmuch-tree.el @@ -17,7 +17,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;; ;; Authors: David Edmondson ;; Mark Walters diff --git a/emacs/notmuch-version.el.tmpl b/emacs/notmuch-version.el.tmpl index 88cc01ce..abf52f17 100644 --- a/emacs/notmuch-version.el.tmpl +++ b/emacs/notmuch-version.el.tmpl @@ -16,7 +16,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;;; Code: diff --git a/emacs/notmuch-wash.el b/emacs/notmuch-wash.el index 065af16f..57e6dfa3 100644 --- a/emacs/notmuch-wash.el +++ b/emacs/notmuch-wash.el @@ -16,7 +16,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;; ;; Authors: Carl Worth ;; David Edmondson diff --git a/emacs/notmuch.el b/emacs/notmuch.el index a8a4c8e5..8acdef37 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -15,7 +15,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;; ;; Authors: Carl Worth ;; Homepage: https://notmuchmail.org/ @@ -26,7 +26,7 @@ ;; ;; You will first need to have the notmuch program installed and have a ;; notmuch database built in order to use this. See -;; http://notmuchmail.org for details. +;; https://notmuchmail.org for details. ;; ;; To install this software, copy it to a directory that is on the ;; `load-path' variable within emacs (a good candidate is @@ -48,7 +48,7 @@ ;; ;; Have fun, and let us know if you have any comment, questions, or ;; kudos: Notmuch list (subscription is not -;; required, but is available from http://notmuchmail.org). +;; required, but is available from https://notmuchmail.org). ;;; Code: diff --git a/hooks.c b/hooks.c index 662629a9..7348d322 100644 --- a/hooks.c +++ b/hooks.c @@ -15,7 +15,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Jani Nikula */ diff --git a/lib/built-with.c b/lib/built-with.c index 7ea1d7fd..635ed3b3 100644 --- a/lib/built-with.c +++ b/lib/built-with.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: David Bremner */ diff --git a/lib/config.cc b/lib/config.cc index e05e50fe..0703b9bb 100644 --- a/lib/config.cc +++ b/lib/config.cc @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: David Bremner */ diff --git a/lib/database-private.h b/lib/database-private.h index 1a78b60e..ca71a92f 100644 --- a/lib/database-private.h +++ b/lib/database-private.h @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/lib/database.cc b/lib/database.cc index 0b604102..86bf2619 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ @@ -217,7 +217,7 @@ typedef struct { /* With these prefix values we follow the conventions published here: * - * http://xapian.org/docs/omega/termprefixes.html + * https://xapian.org/docs/omega/termprefixes.html * * as much as makes sense. Note that I took some liberty in matching * the reserved prefix values to notmuch concepts, (for example, 'G' diff --git a/lib/directory.cc b/lib/directory.cc index 78637b3a..a19f7779 100644 --- a/lib/directory.cc +++ b/lib/directory.cc @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/lib/filenames.c b/lib/filenames.c index 4f7c0d85..63e737dd 100644 --- a/lib/filenames.c +++ b/lib/filenames.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/lib/gen-version-script.sh b/lib/gen-version-script.sh index 0f908093..5621f2a9 100644 --- a/lib/gen-version-script.sh +++ b/lib/gen-version-script.sh @@ -2,7 +2,7 @@ set -eu # we go through a bit of work to get the unmangled names of the # typeinfo symbols because of -# http://sourceware.org/bugzilla/show_bug.cgi?id=10326 +# https://sourceware.org/bugzilla/show_bug.cgi?id=10326 if [ $# -lt 2 ]; then echo Usage: $0 header obj1 obj2 obj3 diff --git a/lib/index.cc b/lib/index.cc index 8aa844e4..8c145540 100644 --- a/lib/index.cc +++ b/lib/index.cc @@ -12,7 +12,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/lib/message-file.c b/lib/message-file.c index ee305202..db18b163 100644 --- a/lib/message-file.c +++ b/lib/message-file.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/lib/message.cc b/lib/message.cc index 02226879..24e698ab 100644 --- a/lib/message.cc +++ b/lib/message.cc @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/lib/messages.c b/lib/messages.c index 0eee5690..b5363bb8 100644 --- a/lib/messages.c +++ b/lib/messages.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 6063262a..3721431e 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/lib/notmuch.h b/lib/notmuch.h index bd977c37..29713ae2 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ @@ -693,7 +693,7 @@ notmuch_database_get_all_tags (notmuch_database_t *db); * completely in the future, but it's likely to be a specialized * version of the general Xapian query syntax: * - * http://xapian.org/docs/queryparser.html + * https://xapian.org/docs/queryparser.html * * As a special case, passing either a length-zero string, (that is ""), * or a string consisting of a single asterisk (that is "*"), will diff --git a/lib/parse-time-vrp.cc b/lib/parse-time-vrp.cc index b15b77c6..dd691494 100644 --- a/lib/parse-time-vrp.cc +++ b/lib/parse-time-vrp.cc @@ -15,7 +15,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Jani Nikula */ diff --git a/lib/parse-time-vrp.h b/lib/parse-time-vrp.h index 3bd12bf3..c024dba2 100644 --- a/lib/parse-time-vrp.h +++ b/lib/parse-time-vrp.h @@ -15,7 +15,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Jani Nikula */ diff --git a/lib/query-fp.cc b/lib/query-fp.cc index 4ffcb1e7..c39f5915 100644 --- a/lib/query-fp.cc +++ b/lib/query-fp.cc @@ -15,7 +15,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: David Bremner */ diff --git a/lib/query-fp.h b/lib/query-fp.h index 67f8705d..d6e4b313 100644 --- a/lib/query-fp.h +++ b/lib/query-fp.h @@ -15,7 +15,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: David Bremner */ diff --git a/lib/query.cc b/lib/query.cc index 7235fde1..7eb73a13 100644 --- a/lib/query.cc +++ b/lib/query.cc @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/lib/sha1.c b/lib/sha1.c index 8a6d4229..b7dea1c2 100644 --- a/lib/sha1.c +++ b/lib/sha1.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/lib/string-list.c b/lib/string-list.c index da72746d..43ebe499 100644 --- a/lib/string-list.c +++ b/lib/string-list.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth * Austin Clements diff --git a/lib/tags.c b/lib/tags.c index b7e5602c..c7d3f66f 100644 --- a/lib/tags.c +++ b/lib/tags.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/lib/thread.cc b/lib/thread.cc index 0c937d76..84ee5298 100644 --- a/lib/thread.cc +++ b/lib/thread.cc @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/mime-node.c b/mime-node.c index e96e6639..c9b82330 100644 --- a/mime-node.c +++ b/mime-node.c @@ -14,7 +14,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Authors: Carl Worth * Keith Packard diff --git a/notmuch-client.h b/notmuch-client.h index c130b937..ebc092b8 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/notmuch-compact.c b/notmuch-compact.c index 93737216..855545d7 100644 --- a/notmuch-compact.c +++ b/notmuch-compact.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Ben Gamari */ diff --git a/notmuch-config.c b/notmuch-config.c index c618f2ca..de9a8a41 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ @@ -27,7 +27,7 @@ static const char toplevel_config_comment[] = " .notmuch-config - Configuration file for the notmuch mail system\n" "\n" - " For more information about notmuch, see http://notmuchmail.org"; + " For more information about notmuch, see https://notmuchmail.org"; static const char database_config_comment[] = " Database configuration\n" diff --git a/notmuch-count.c b/notmuch-count.c index 0b6e6f54..35a2aa70 100644 --- a/notmuch-count.c +++ b/notmuch-count.c @@ -14,7 +14,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Keith Packard */ diff --git a/notmuch-dump.c b/notmuch-dump.c index 8e795115..cae1db8a 100644 --- a/notmuch-dump.c +++ b/notmuch-dump.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/notmuch-emacs-mua b/notmuch-emacs-mua index 4404cd7c..02d7fd74 100755 --- a/notmuch-emacs-mua +++ b/notmuch-emacs-mua @@ -15,7 +15,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see http://www.gnu.org/licenses/ . +# along with this program. If not, see https://www.gnu.org/licenses/ . # # Authors: Jani Nikula # diff --git a/notmuch-insert.c b/notmuch-insert.c index 5205c17a..131f09e2 100644 --- a/notmuch-insert.c +++ b/notmuch-insert.c @@ -16,7 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Peter Wang */ diff --git a/notmuch-new.c b/notmuch-new.c index 8d1545e1..799fec20 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/notmuch-reply.c b/notmuch-reply.c index 457e54ab..49513732 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -14,7 +14,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Authors: Carl Worth * Keith Packard diff --git a/notmuch-restore.c b/notmuch-restore.c index 8ad67b5e..371237c5 100644 --- a/notmuch-restore.c +++ b/notmuch-restore.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/notmuch-search.c b/notmuch-search.c index 6d08c250..8c65d5ad 100644 --- a/notmuch-search.c +++ b/notmuch-search.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/notmuch-setup.c b/notmuch-setup.c index 9aaf9286..9a66810d 100644 --- a/notmuch-setup.c +++ b/notmuch-setup.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/notmuch-show.c b/notmuch-show.c index 3d5033d6..22fa655a 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/notmuch-tag.c b/notmuch-tag.c index 0d153282..18d78ddd 100644 --- a/notmuch-tag.c +++ b/notmuch-tag.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/notmuch-time.c b/notmuch-time.c index e250c3d5..2734b36a 100644 --- a/notmuch-time.c +++ b/notmuch-time.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/notmuch.c b/notmuch.c index ce6c5756..38b73c1d 100644 --- a/notmuch.c +++ b/notmuch.c @@ -14,7 +14,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Authors: Carl Worth * Keith Packard @@ -354,7 +354,7 @@ notmuch_command (notmuch_config_t *config, "You can also use \"notmuch show\" with any of the thread IDs resulting\n" "from a search. Finally, you may want to explore using a more sophisticated\n" "interface to notmuch such as the emacs interface implemented in notmuch.el\n" - "or any other interface described at http://notmuchmail.org\n\n" + "or any other interface described at https://notmuchmail.org\n\n" "And don't forget to run \"notmuch new\" whenever new mail arrives.\n\n" "Have fun, and may your inbox never have much mail.\n\n", notmuch_config_get_user_name (config), diff --git a/packaging/fedora/notmuch.spec b/packaging/fedora/notmuch.spec index 79994c9a..5146edd3 100644 --- a/packaging/fedora/notmuch.spec +++ b/packaging/fedora/notmuch.spec @@ -21,9 +21,9 @@ Summary: Thread-based email index, search and tagging Group: Applications/Internet License: GPLv3+ -URL: http://notmuchmail.org/ +URL: https://notmuchmail.org/ -Source0: http://notmuchmail.org/releases/notmuch-%{version}.tar.gz +Source0: https://notmuchmail.org/releases/notmuch-%{version}.tar.gz BuildRequires: xapian-core-devel gmime-devel libtalloc-devel BuildRequires: zlib-devel emacs-el emacs-nox python ruby ruby-devel perl diff --git a/parse-time-string/parse-time-string.c b/parse-time-string/parse-time-string.c index 1cef47d4..48ec5b0c 100644 --- a/parse-time-string/parse-time-string.c +++ b/parse-time-string/parse-time-string.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . * * Author: Jani Nikula */ diff --git a/parse-time-string/parse-time-string.h b/parse-time-string/parse-time-string.h index bfa4ee35..2063bcad 100644 --- a/parse-time-string/parse-time-string.h +++ b/parse-time-string/parse-time-string.h @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . * * Author: Jani Nikula */ diff --git a/performance-test/Makefile.local b/performance-test/Makefile.local index 3469aa3d..9dc260e3 100644 --- a/performance-test/Makefile.local +++ b/performance-test/Makefile.local @@ -10,7 +10,7 @@ MEMORY_TEST_SCRIPT := ${dir}/notmuch-memory-test CORPUS_NAME := notmuch-email-corpus-$(PERFTEST_VERSION).tar.xz TXZFILE := ${dir}/download/${CORPUS_NAME} SIGFILE := ${TXZFILE}.asc -DEFAULT_URL := http://notmuchmail.org/releases/${CORPUS_NAME} +DEFAULT_URL := https://notmuchmail.org/releases/${CORPUS_NAME} perf-test: time-test memory-test @@ -32,7 +32,7 @@ setup-perf-test: $(TXZFILE) $(TXZFILE): @printf "\nPlease download ${TXZFILE} using:\n\n" @printf "\t%% make download-corpus\n\n" - @echo or see http://notmuchmail.org/corpus for download locations + @echo or see https://notmuchmail.org/corpus for download locations @echo @false diff --git a/performance-test/README b/performance-test/README index 996724cd..fbc61028 100644 --- a/performance-test/README +++ b/performance-test/README @@ -37,7 +37,7 @@ To fetch the actual corpus it should work to run In case that fails or is too slow, check - http://notmuchmail.org/corpus + https://notmuchmail.org/corpus for a list of mirrors. diff --git a/query-string.c b/query-string.c index 65365123..cc8b27de 100644 --- a/query-string.c +++ b/query-string.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/sprinter-sexp.c b/sprinter-sexp.c index 0aa51e8b..08783e11 100644 --- a/sprinter-sexp.c +++ b/sprinter-sexp.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Peter Feigl */ diff --git a/test/T310-emacs.sh b/test/T310-emacs.sh index daa02568..65c1728d 100755 --- a/test/T310-emacs.sh +++ b/test/T310-emacs.sh @@ -760,8 +760,8 @@ bought inbox,stashtest ${gen_msg_filename} http://mid.gmane.org/bought -http://marc.info/?i=bought -http://mid.mail-archive.com/bought +https://marc.info/?i=bought +https://mid.mail-archive.com/bought EOF test_expect_equal_file OUTPUT EXPECTED diff --git a/test/T360-symbol-hiding.sh b/test/T360-symbol-hiding.sh index 3f18ec1a..e681d5b3 100755 --- a/test/T360-symbol-hiding.sh +++ b/test/T360-symbol-hiding.sh @@ -5,7 +5,7 @@ # This test tests whether hiding Xapian::Error symbols in libnotmuch # also hides them for other users of libxapian. This is motivated by -# the discussion in http://gcc.gnu.org/wiki/Visibility' +# the discussion in https://gcc.gnu.org/wiki/Visibility' test_description='exception symbol hiding' diff --git a/test/database-test.c b/test/database-test.c index b8c3a67c..42f66559 100644 --- a/test/database-test.c +++ b/test/database-test.c @@ -15,7 +15,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: David Bremner */ diff --git a/test/parse-time.c b/test/parse-time.c index 901a4dde..694761cf 100644 --- a/test/parse-time.c +++ b/test/parse-time.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see . + * along with this program. If not, see . * * Author: Jani Nikula */ diff --git a/test/random-corpus.c b/test/random-corpus.c index d74271d9..aca694a3 100644 --- a/test/random-corpus.c +++ b/test/random-corpus.c @@ -19,7 +19,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: David Bremner */ diff --git a/test/smtp-dummy.c b/test/smtp-dummy.c index bb136687..a629927c 100644 --- a/test/smtp-dummy.c +++ b/test/smtp-dummy.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Authors: Carl Worth */ diff --git a/test/test-databases/Makefile.local b/test/test-databases/Makefile.local index ff333a1d..dcc8863c 100644 --- a/test/test-databases/Makefile.local +++ b/test/test-databases/Makefile.local @@ -1,6 +1,6 @@ # -*- makefile -*- -TEST_DATABASE_MIRROR=http://notmuchmail.org/releases/test-databases +TEST_DATABASE_MIRROR=https://notmuchmail.org/releases/test-databases dir := test/test-databases diff --git a/test/test-lib-common.sh b/test/test-lib-common.sh index ba4a8e11..fe92cdd3 100644 --- a/test/test-lib-common.sh +++ b/test/test-lib-common.sh @@ -13,7 +13,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see http://www.gnu.org/licenses/ . +# along with this program. If not, see https://www.gnu.org/licenses/ . # This file contains common code to be used by both the regular # (correctness) tests and the performance tests. diff --git a/test/test-lib.el b/test/test-lib.el index 596a7051..496626f6 100644 --- a/test/test-lib.el +++ b/test/test-lib.el @@ -16,7 +16,7 @@ ;; General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License -;; along with Notmuch. If not, see . +;; along with Notmuch. If not, see . ;; ;; Authors: Dmitry Kurochkin diff --git a/test/test-lib.sh b/test/test-lib.sh index 201d0ebb..29ec0083 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -13,7 +13,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see http://www.gnu.org/licenses/ . +# along with this program. If not, see https://www.gnu.org/licenses/ . if [ ${BASH_VERSINFO[0]} -lt 4 ]; then echo "Error: The notmuch test suite requires a bash version >= 4.0" diff --git a/util/error_util.c b/util/error_util.c index d6e60fc9..778bbd52 100644 --- a/util/error_util.c +++ b/util/error_util.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/util/error_util.h b/util/error_util.h index 17c8727d..4bb338a2 100644 --- a/util/error_util.h +++ b/util/error_util.h @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/util/hex-escape.c b/util/hex-escape.c index b4a2a02a..8883ff90 100644 --- a/util/hex-escape.c +++ b/util/hex-escape.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: David Bremner */ diff --git a/util/string-util.c b/util/string-util.c index 92af937f..18125309 100644 --- a/util/string-util.c +++ b/util/string-util.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Jani Nikula */ diff --git a/util/xutil.c b/util/xutil.c index ac496daf..f211eaaa 100644 --- a/util/xutil.c +++ b/util/xutil.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/util/xutil.h b/util/xutil.h index b84e0e25..4829f33c 100644 --- a/util/xutil.h +++ b/util/xutil.h @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: Carl Worth */ diff --git a/util/zlib-extra.c b/util/zlib-extra.c index 2e704457..2b2cd8f9 100644 --- a/util/zlib-extra.c +++ b/util/zlib-extra.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see http://www.gnu.org/licenses/ . + * along with this program. If not, see https://www.gnu.org/licenses/ . * * Author: David Bremner */ From ba0b95f846820b1d71f67731a8c38ac2866c921a Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sat, 4 Jun 2016 19:17:41 -0300 Subject: [PATCH 035/179] lib: document config metadata This probably should have been part of 3458e3c89c, but I missed it. --- lib/database.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/database.cc b/lib/database.cc index 86bf2619..2b2d8219 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -185,6 +185,14 @@ typedef struct { * generated is 1 and the value will be * incremented for each thread ID. * + * C* metadata keys starting with C indicate + * configuration data. It can be managed with the + * n_database_*config* API. There is a convention + * of hierarchical keys separated by '.' (e.g. + * query.notmuch stores the value for the named + * query 'notmuch'), but it is not enforced by the + * API. + * * Obsolete metadata * ----------------- * From d9d2d535b376b4eed455b4d5ca864ae00ee0f4a7 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 5 Jun 2016 19:23:58 -0300 Subject: [PATCH 036/179] doc: document notmuch-dump header line This was introduced with the libconfig changes, but not documented then. --- doc/man1/notmuch-dump.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/doc/man1/notmuch-dump.rst b/doc/man1/notmuch-dump.rst index eda9e07f..94986a86 100644 --- a/doc/man1/notmuch-dump.rst +++ b/doc/man1/notmuch-dump.rst @@ -86,7 +86,15 @@ Supported options for **dump** include Output per-message metadata, namely tags. See *format* above for description of the output. - The default is to include both tags and configuration information + The default is to include both tags and configuration + information. As of version 2 of the dump format, there is a + header line of the following form + + | + | #notmuch-dump <*format*>:<*version*> <*included*> + + where <*included*> is a comma separated list of the above + options. ``--output=``\ Write output to given file instead of stdout. From d080b4100aedbb93a4a4b43f6d07fa2daba101d3 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Thu, 2 Jun 2016 12:01:27 -0400 Subject: [PATCH 037/179] doc: clean up boolean vs. probabilistic prefixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sphinx-build emits a minor warning: [...]doc/man7/notmuch-search-terms.rst:223: WARNING: Block quote ends without a blank line; unexpected unindent. And the tabular representation of boolean or probabilistic prefixes currently renders like this when i view it in man: ┌───────────────────────────┬────────────────────────────┐ │Boolean │ Probabilistic │ └───────────────────────────┴────────────────────────────┘ │ tag: id: │ from: to: │ │ │ │ │ thread: folder: │ subject: attach‐ │ │ path: │ ment: mimetype: │ └───────────────────────────┴────────────────────────────┘ This isn't just ugly: it's confusing, because it seems to imply that some of the prefixes in the left-hand column are somehow related to specific other prefixes in the right-hand column. The Definition List representation introduced by this patch should be simpler for readers to understand, and doesn't have the warning. --- doc/man7/notmuch-search-terms.rst | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/doc/man7/notmuch-search-terms.rst b/doc/man7/notmuch-search-terms.rst index 223031b8..075f88c8 100644 --- a/doc/man7/notmuch-search-terms.rst +++ b/doc/man7/notmuch-search-terms.rst @@ -215,15 +215,11 @@ Boolean and Probabilistic Prefixes Xapian (and hence notmuch) prefixes are either **boolean**, supporting exact matches like "tag:inbox" or **probabilistic**, supporting a more flexible **term** based searching. The prefixes currently supported by notmuch are as follows. -+------------------+-----------------------+ -|Boolean |Probabilistic | -+------------------+-----------------------+ -| **tag:** **id:** | **from:** **to:** | -|**thread:** |**subject:** | -|**folder:** |**attachment:** | -|**path:** |**mimetype:** | -| | | -+------------------+-----------------------+ + +Boolean + **tag:**, **id:**, **thread:**, **folder:**, **path:** +Probabilistic + **from:**, **to:**, **subject:**, **attachment:**, **mimetype:** Terms and phrases ----------------- From 4291f326809692bad61b50f942dc366e59ab30ef Mon Sep 17 00:00:00 2001 From: David Bremner Date: Thu, 9 Jun 2016 23:35:27 -0300 Subject: [PATCH 038/179] lib: fix memory leak of field processor objects The field processor objects need to be deallocated explicitly just like the range processors (or a talloc destructor defined). --- lib/database.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/database.cc b/lib/database.cc index 2b2d8219..afafe88c 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -1107,6 +1107,13 @@ notmuch_database_close (notmuch_database_t *notmuch) delete notmuch->last_mod_range_processor; notmuch->last_mod_range_processor = NULL; +#if HAVE_XAPIAN_FIELD_PROCESSOR + delete notmuch->date_field_processor; + notmuch->date_field_processor = NULL; + delete notmuch->query_field_processor; + notmuch->query_field_processor = NULL; +#endif + return status; } From 1e96445cb5dc333e22b9ce01245902ba23fdb437 Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Thu, 9 Jun 2016 22:35:12 +0300 Subject: [PATCH 039/179] devel/man-to-mdwn.pl: portable locale environment variable setting Setting locale environment variables (LC_* and LANG) to e.g. en_US.utf8 works fine on Linux, and that is what locale -a returns (in Linux). However this does not work e.g. in some *BSD systems. In these systems, en_US.UTF-8 works. This also works in Linux systems (which may look like a surprising thing on the first sight(*)). But that *UTF-8 format seems to be widely used in the Linux system: Grep it through the files in /etc/, for example. Easy way to test: Run the following command lines. First should complain about setting locale failed, and second should not. $ LC_ALL=en_US.UTF-1 perl -e '' $ LC_ALL=en_US.UTF-8 perl -e '' (*) and who knows what the "standard" is... --- devel/man-to-mdwn.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devel/man-to-mdwn.pl b/devel/man-to-mdwn.pl index f9d31b73..a3c40695 100755 --- a/devel/man-to-mdwn.pl +++ b/devel/man-to-mdwn.pl @@ -66,7 +66,7 @@ while (my ($k, $v) = each %fhash) my @lines; open I, '-|', qw/env -i/, "PATH=$ENV{PATH}", - qw/TERM=vt100 LANG=en_US.utf8 LC_ALL=en_US.utf8/, + qw/TERM=vt100 LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8/, qw/GROFF_NO_SGR=1 MAN_KEEP_FORMATTING=1 MANWIDTH=80/, qw/man/, $v or die "$!"; binmode I, ':utf8'; From 44cfa90bdc4d814217b05fdc36f1595c9b54d729 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 7 Jun 2016 07:37:57 -0300 Subject: [PATCH 040/179] lib: fix definition of LIBNOTMUCH_CHECK_VERSION Fix bug reported in id:20160606124522.g2y2eazhhrwjsa4h@flatcap.org Although the C99 standard 6.10 is a little non-obvious on this point, the docs for e.g. gcc are unambiguous. And indeed in practice with the extra space, this code fails #include #define foo (x) (x+1) int main(int argc, char **argv){ printf("%d\n",foo(1)); } --- lib/notmuch.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/notmuch.h b/lib/notmuch.h index 29713ae2..d4a97cb8 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -93,7 +93,7 @@ NOTMUCH_BEGIN_DECLS * #endif * @endcode */ -#define LIBNOTMUCH_CHECK_VERSION (major, minor, micro) \ +#define LIBNOTMUCH_CHECK_VERSION(major, minor, micro) \ (LIBNOTMUCH_MAJOR_VERSION > (major) || \ (LIBNOTMUCH_MAJOR_VERSION == (major) && LIBNOTMUCH_MINOR_VERSION > (minor)) || \ (LIBNOTMUCH_MAJOR_VERSION == (major) && LIBNOTMUCH_MINOR_VERSION == (minor) && \ From 6a49e8a51e0591d92ae187af80cf1bedfdd8e09b Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Sun, 5 Jun 2016 23:38:05 +0300 Subject: [PATCH 041/179] doc/notmuch-emacs.rst: update notmuch-hello buffer help text To look the same as commit b8a136187a3b changed it in emacs MUA code. --- doc/notmuch-emacs.rst | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/doc/notmuch-emacs.rst b/doc/notmuch-emacs.rst index 6f2f61e9..d68542d3 100644 --- a/doc/notmuch-emacs.rst +++ b/doc/notmuch-emacs.rst @@ -42,11 +42,8 @@ a mouse or by positioning the cursor and pressing ```` | | All tags: **[show]** | -| Type a search query and hit RET to view matching threads. -| Edit saved searches with the ``edit`` button. -| Hit RET or click on a saved search or tag name to view matching threads. -| ``=`` to refresh this screen. ``s`` to search messages. ``q`` to quit. -| **Customize** this page. +| Hit \`?' for context-sensitive help in any Notmuch screen. +| Customize Notmuch or this page. You can change the overall appearance of the notmuch-hello screen by customizing the variable :index:`notmuch-hello-sections`. From 1ca752fa362e7c245d88b6cc742d178824f2a50c Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Sat, 28 May 2016 15:39:28 +0300 Subject: [PATCH 042/179] test-lib.sh: renamed die...()s to trap...()s and exit...() Now the function names more accurately describes what the functions do. --- test/test-lib.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/test-lib.sh b/test/test-lib.sh index 29ec0083..05e203d7 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -223,15 +223,15 @@ test_fixed=0 test_broken=0 test_success=0 -_die_common () { +_exit_common () { code=$? trap - EXIT set +ex rm -rf "$TEST_TMPDIR" } -die () { - _die_common +trap_exit () { + _exit_common if test -n "$GIT_EXIT_OK" then exit $code @@ -245,8 +245,8 @@ die () { fi } -die_signal () { - _die_common +trap_signal () { + _exit_common echo >&6 "FATAL: $0: interrupted by signal" $((code - 128)) exit $code } @@ -254,8 +254,8 @@ die_signal () { GIT_EXIT_OK= # Note: TEST_TMPDIR *NOT* exported! TEST_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/notmuch-test-$$.XXXXXX") -trap 'die' EXIT -trap 'die_signal' HUP INT TERM +trap 'trap_exit' EXIT +trap 'trap_signal' HUP INT TERM test_decode_color () { sed -e 's/.\[1m//g' \ From d53b417d1f5935a924b0f0127e64750c8448ac3a Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Sat, 28 May 2016 15:39:29 +0300 Subject: [PATCH 043/179] test: add function die () and have use of it in add_email_corpus () Added die() function to test-lib.sh with the following first use of it: If notmuch new fails during email corpus addition the database is most probably inexistent or broken and the added corpus would be unusable while running single tests, giving misleading failures ("only" full 'make test' cleans out old corpus). --- test/test-lib.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/test-lib.sh b/test/test-lib.sh index 05e203d7..4a8ef1f4 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -251,6 +251,16 @@ trap_signal () { exit $code } +die () { + _exit_common + exec >&6 + say_color error '%-6s' FATAL + echo " $*" + echo + echo "Unexpected exit while executing $0." + exit 1 +} + GIT_EXIT_OK= # Note: TEST_TMPDIR *NOT* exported! TEST_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/notmuch-test-$$.XXXXXX") @@ -544,7 +554,7 @@ add_email_corpus () cp -a $TEST_DIRECTORY/corpus.mail ${MAIL_DIR} else cp -a $TEST_DIRECTORY/corpus ${MAIL_DIR} - notmuch new >/dev/null + notmuch new >/dev/null || die "'notmuch new' failed while adding email corpus" cp -a ${MAIL_DIR} $TEST_DIRECTORY/corpus.mail fi } From 36492f20a71906413d9922dbad5e18571059f57a Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Sat, 28 May 2016 15:39:30 +0300 Subject: [PATCH 044/179] test: fix die() in test-lib-common.sh In scripts that include test-lib-common.sh but not test-lib.sh the die() implementation needs to be a bit different due to fd redirection differences. test-lib-common.sh implements die() only if it was not implemented already. --- test/test-lib-common.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/test-lib-common.sh b/test/test-lib-common.sh index fe92cdd3..0fdeeb72 100644 --- a/test/test-lib-common.sh +++ b/test/test-lib-common.sh @@ -18,6 +18,12 @@ # This file contains common code to be used by both the regular # (correctness) tests and the performance tests. +# test-lib.sh defines die() which echoes to nonstandard fd where +# output was redirected earlier in that file. If test-lib.sh is not +# loaded, neither this redirection nor die() function were defined. +# +type die >/dev/null 2>&1 || die () { echo "$@" >&2; exit 1; } + find_notmuch_path () { dir="$1" From e401daecab43ff974474319d1f996c03a076d319 Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Sat, 28 May 2016 14:06:03 +0300 Subject: [PATCH 045/179] notmuch-emacs-mua: use message-add-actions Use message.el -provided function message-add-actions to set(/add) #'save-buffers-kill-terminal into the message-exit-actions list. --- notmuch-emacs-mua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notmuch-emacs-mua b/notmuch-emacs-mua index 02d7fd74..08be145b 100755 --- a/notmuch-emacs-mua +++ b/notmuch-emacs-mua @@ -132,7 +132,7 @@ done # Kill the terminal/frame if we're creating one. if [ -z "$USE_EMACSCLIENT" -o -n "$CREATE_FRAME" -o -n "$NO_WINDOW" ]; then - ELISP="${ELISP} (setq message-exit-actions (list #'save-buffers-kill-terminal))" + ELISP="${ELISP} (message-add-action #'save-buffers-kill-terminal 'exit)" fi # End progn. From 65c219560f33d41f094a204c631fb5aa0ff3395e Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Sat, 28 May 2016 14:06:04 +0300 Subject: [PATCH 046/179] notmuch-emacs-mua: escape $PWD (and cd always) Escaping $PWD makes this work in directories like 'foo"bar'... Cd'ing always makes the working directory to be consistent whether --body option was used or not (when using emacsclient, but cd'ing when using emacs does not cause any harm). Note that documentation of `insert-file` expects programs to call `insert-file-contents` instead. In our simple case `insert-file` works better as it does some good checks that we'd have to implement ourselves. Look lisp/files.el in emacs sources for more information. --- notmuch-emacs-mua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/notmuch-emacs-mua b/notmuch-emacs-mua index 08be145b..f9d83713 100755 --- a/notmuch-emacs-mua +++ b/notmuch-emacs-mua @@ -39,8 +39,10 @@ USE_EMACSCLIENT= AUTO_DAEMON= CREATE_FRAME= +escape -v pwd "$PWD" + # The crux of it all: construct an elisp progn and eval it. -ELISP="(prog1 'done (require 'notmuch) (notmuch-mua-new-mail)" +ELISP="(prog1 'done (require 'notmuch) (cd \"$pwd\") (notmuch-mua-new-mail)" # Short options compatible with mutt(1). while getopts :s:c:b:i:h opt; do @@ -95,7 +97,7 @@ while getopts :s:c:b:i:h opt; do ELISP="${ELISP} (message-goto-bcc) (insert \"${OPTARG}, \")" ;; --body|i) - ELISP="${ELISP} (message-goto-body) (cd \"${PWD}\") (insert-file \"${OPTARG}\")" + ELISP="${ELISP} (message-goto-body) (insert-file \"${OPTARG}\")" ;; --print) PRINT_ONLY=1 From f0e5317bcdc856739c5107705e3bc86b31b01d38 Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Thu, 26 May 2016 00:37:40 +0300 Subject: [PATCH 047/179] test: set LD_LIBRARY_PATH early and keep its old contents Previously LD_LIBRARY_PATH was exported (and environment changed) in the middle of test case execution, when a function setting it was called. Previously the old contents of LD_LIBRARY_PATH was lost (if any) when it was re-set and exported. In some systems the old contents of LD_LIBRARY_PATH was needed to e.g. locate suitable gmime library. --- test/T360-symbol-hiding.sh | 5 ++--- test/test-lib-common.sh | 5 +++++ test/test-lib.sh | 3 --- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/test/T360-symbol-hiding.sh b/test/T360-symbol-hiding.sh index e681d5b3..b3dbb1b5 100755 --- a/test/T360-symbol-hiding.sh +++ b/test/T360-symbol-hiding.sh @@ -13,9 +13,8 @@ test_description='exception symbol hiding' test_begin_subtest 'running test' run_test mkdir -p ${PWD}/fakedb/.notmuch -( LD_LIBRARY_PATH="$TEST_DIRECTORY/../lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \ - $TEST_DIRECTORY/symbol-test ${PWD}/fakedb ${PWD}/nonexistent \ - 2>&1 | notmuch_dir_sanitize | sed -e "s,\`,\',g" -e "s,${NOTMUCH_DEFAULT_XAPIAN_BACKEND},backend,g") > OUTPUT +$TEST_DIRECTORY/symbol-test ${PWD}/fakedb ${PWD}/nonexistent 2>&1 \ + | notmuch_dir_sanitize | sed -e "s,\`,\',g" -e "s,${NOTMUCH_DEFAULT_XAPIAN_BACKEND},backend,g" > OUTPUT cat < EXPECTED A Xapian exception occurred opening database: Couldn't stat 'CWD/fakedb/.notmuch/xapian' diff --git a/test/test-lib-common.sh b/test/test-lib-common.sh index 0fdeeb72..03ef1d2d 100644 --- a/test/test-lib-common.sh +++ b/test/test-lib-common.sh @@ -58,6 +58,11 @@ restore_database () { TEST_DIRECTORY=$(pwd -P) notmuch_path=`find_notmuch_path "$TEST_DIRECTORY"` +# Prepend $TEST_DIRECTORY/../lib to LD_LIBRARY_PATH, to make tests work +# on systems where ../notmuch depends on LD_LIBRARY_PATH. +LD_LIBRARY_PATH=${TEST_DIRECTORY%/*}/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} +export LD_LIBRARY_PATH + # configure output . $notmuch_path/sh.config || exit 1 diff --git a/test/test-lib.sh b/test/test-lib.sh index 4a8ef1f4..b0ee253c 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -1192,7 +1192,6 @@ test_emacs () { } test_python() { - export LD_LIBRARY_PATH=$TEST_DIRECTORY/../lib export PYTHONPATH=$TEST_DIRECTORY/../bindings/python (echo "import sys; _orig_stdout=sys.stdout; sys.stdout=open('OUTPUT', 'w')"; cat) \ @@ -1200,7 +1199,6 @@ test_python() { } test_ruby() { - export LD_LIBRARY_PATH=$TEST_DIRECTORY/../lib MAIL_DIR=$MAIL_DIR ruby -I $TEST_DIRECTORY/../bindings/ruby> OUTPUT } @@ -1208,7 +1206,6 @@ test_C () { exec_file="test${test_count}" test_file="${exec_file}.c" cat > ${test_file} - export LD_LIBRARY_PATH=${TEST_DIRECTORY}/../lib ${TEST_CC} ${TEST_CFLAGS} -I${TEST_DIRECTORY}/../lib -o ${exec_file} ${test_file} -L${TEST_DIRECTORY}/../lib/ -lnotmuch -ltalloc echo "== stdout ==" > OUTPUT.stdout echo "== stderr ==" > OUTPUT.stderr From 2333a44ab7280a286af38e589a4bbc712bc3589a Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Thu, 26 May 2016 00:37:41 +0300 Subject: [PATCH 048/179] test: test_python: set PYTHONPATH to the python execution environment Place PYTHONPATH to the environment when python is executed in a way that current shell environment is not affected. This also allows adding the old value of PYTHONPATH to the end of the new value (otherwise it would have been appended again and again when test_python is called). At the same time, use -B option to avoid writing .pyc files to bindings/python/* (which are not cleared out by distclean). Drop the (unused) prefix code which preserved the original stdout of the python program and opened sys.stdout to OUTPUT. In place of that there is now note how (debug) information can be printed to original stdout. --- test/test-lib.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test-lib.sh b/test/test-lib.sh index b0ee253c..54c65b60 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -1192,10 +1192,10 @@ test_emacs () { } test_python() { - export PYTHONPATH=$TEST_DIRECTORY/../bindings/python - - (echo "import sys; _orig_stdout=sys.stdout; sys.stdout=open('OUTPUT', 'w')"; cat) \ - | $NOTMUCH_PYTHON - + # Note: if there is need to print debug information from python program, + # use stdout = os.fdopen(6, 'w') or stderr = os.fdopen(7, 'w') + PYTHONPATH="$TEST_DIRECTORY/../bindings/python${PYTHONPATH:+:$PYTHONPATH}" \ + $NOTMUCH_PYTHON -B - > OUTPUT } test_ruby() { From 57bd4cf3221a56f8fd8a27af541d6cc8b4011b62 Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Thu, 26 May 2016 00:04:30 +0300 Subject: [PATCH 049/179] Makefile.local: set IS_GIT = yes when regular file $srcdir/.git exists $srcdir/.git may also be file. E.g. `git worktree` creates .git file while new working tree is populated. --- Makefile.local | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.local b/Makefile.local index cf16185e..f2ad0c12 100644 --- a/Makefile.local +++ b/Makefile.local @@ -10,7 +10,7 @@ # repository), we let git append identification of the actual commit. PACKAGE=notmuch -IS_GIT=$(shell if [ -d ${srcdir}/.git ] ; then echo yes ; else echo no; fi) +IS_GIT=$(shell if [ -d ${srcdir}/.git -o -f ${srcdir}/.git ] ; then echo yes ; else echo no; fi) ifeq ($(IS_GIT),yes) DATE:=$(shell git --git-dir=${srcdir}/.git log --date=short -1 --pretty=format:%cd) From cc1a6d2a947ef8c2577a77027d69fce012ae91fd Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sat, 4 Jun 2016 09:29:14 -0300 Subject: [PATCH 050/179] test: factor out some boilerplate from C tests The trick of having a common header file doesn't work to share between test scripts, so make an include file in the test directory. The use of #include looks slightly pretentious, but the include file is not actually in the current (temporary) directory. --- test/T590-libconfig.sh | 39 ++++++++++++++------------------------- test/notmuch-test.h | 16 ++++++++++++++++ test/test-lib.sh | 2 +- 3 files changed, 31 insertions(+), 26 deletions(-) create mode 100644 test/notmuch-test.h diff --git a/test/T590-libconfig.sh b/test/T590-libconfig.sh index 9c1e566d..e8c078d5 100755 --- a/test/T590-libconfig.sh +++ b/test/T590-libconfig.sh @@ -6,20 +6,9 @@ test_description="library config API" add_email_corpus cat < c_head -#include #include #include -#include - -void run(int line, notmuch_status_t ret) -{ - if (ret) { - fprintf (stderr, "line %d: %s\n", line, ret); - exit (1); - } -} - -#define RUN(v) run(__LINE__, v); +#include int main (int argc, char** argv) { @@ -27,23 +16,23 @@ int main (int argc, char** argv) char *val; notmuch_status_t stat; - RUN(notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db)); + EXPECT0(notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db)); EOF cat < c_tail - RUN(notmuch_database_destroy(db)); + EXPECT0(notmuch_database_destroy(db)); } EOF test_begin_subtest "notmuch_database_{set,get}_config" cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} { - RUN(notmuch_database_set_config (db, "testkey1", "testvalue1")); - RUN(notmuch_database_set_config (db, "testkey2", "testvalue2")); - RUN(notmuch_database_get_config (db, "testkey1", &val)); + EXPECT0(notmuch_database_set_config (db, "testkey1", "testvalue1")); + EXPECT0(notmuch_database_set_config (db, "testkey2", "testvalue2")); + EXPECT0(notmuch_database_get_config (db, "testkey1", &val)); printf("testkey1 = %s\n", val); - RUN(notmuch_database_get_config (db, "testkey2", &val)); + EXPECT0(notmuch_database_get_config (db, "testkey2", &val)); printf("testkey2 = %s\n", val); } EOF @@ -60,7 +49,7 @@ test_begin_subtest "notmuch_database_get_config_list: empty list" cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} { notmuch_config_list_t *list; - RUN(notmuch_database_get_config_list (db, "nonexistent", &list)); + EXPECT0(notmuch_database_get_config_list (db, "nonexistent", &list)); printf("valid = %d\n", notmuch_config_list_valid (list)); notmuch_config_list_destroy (list); } @@ -77,9 +66,9 @@ test_begin_subtest "notmuch_database_get_config_list: all pairs" cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} { notmuch_config_list_t *list; - RUN(notmuch_database_set_config (db, "zzzafter", "afterval")); - RUN(notmuch_database_set_config (db, "aaabefore", "beforeval")); - RUN(notmuch_database_get_config_list (db, "", &list)); + EXPECT0(notmuch_database_set_config (db, "zzzafter", "afterval")); + EXPECT0(notmuch_database_set_config (db, "aaabefore", "beforeval")); + EXPECT0(notmuch_database_get_config_list (db, "", &list)); for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) { printf("%s %s\n", notmuch_config_list_key (list), notmuch_config_list_value(list)); } @@ -100,7 +89,7 @@ test_begin_subtest "notmuch_database_get_config_list: one prefix" cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} { notmuch_config_list_t *list; - RUN(notmuch_database_get_config_list (db, "testkey", &list)); + EXPECT0(notmuch_database_get_config_list (db, "testkey", &list)); for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) { printf("%s %s\n", notmuch_config_list_key (list), notmuch_config_list_value(list)); } @@ -118,7 +107,7 @@ test_expect_equal_file EXPECTED OUTPUT test_begin_subtest "dump config" cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} { - RUN(notmuch_database_set_config (db, "key with spaces", "value, with, spaces!")); + EXPECT0(notmuch_database_set_config (db, "key with spaces", "value, with, spaces!")); } EOF notmuch dump --include=config >OUTPUT @@ -136,7 +125,7 @@ test_begin_subtest "restore config" notmuch dump --include=config >EXPECTED cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} { - RUN(notmuch_database_set_config (db, "testkey1", "mutatedvalue")); + EXPECT0(notmuch_database_set_config (db, "testkey1", "mutatedvalue")); } EOF notmuch restore --include=config +#include + +inline static void +expect0(int line, notmuch_status_t ret) +{ + if (ret) { + fprintf (stderr, "line %d: %s\n", line, ret); + exit (1); + } +} + +#define EXPECT0(v) expect0(__LINE__, v); +#endif diff --git a/test/test-lib.sh b/test/test-lib.sh index 54c65b60..aac0343b 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -1206,7 +1206,7 @@ test_C () { exec_file="test${test_count}" test_file="${exec_file}.c" cat > ${test_file} - ${TEST_CC} ${TEST_CFLAGS} -I${TEST_DIRECTORY}/../lib -o ${exec_file} ${test_file} -L${TEST_DIRECTORY}/../lib/ -lnotmuch -ltalloc + ${TEST_CC} ${TEST_CFLAGS} -I${TEST_DIRECTORY} -I${TEST_DIRECTORY}/../lib -o ${exec_file} ${test_file} -L${TEST_DIRECTORY}/../lib/ -lnotmuch -ltalloc echo "== stdout ==" > OUTPUT.stdout echo "== stderr ==" > OUTPUT.stderr ./${exec_file} "$@" 1>>OUTPUT.stdout 2>>OUTPUT.stderr From dd5cd6964fe074c79c1850abb82d805bca35a5c5 Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Thu, 9 Jun 2016 10:54:10 +0100 Subject: [PATCH 051/179] emacs: show: improve handling of mark read tagging errors Previously if a marking read tag change (i.e., removing the unread tag) failed for some reason, such as a locked database, then no more mark read tag changes would be attempted in that buffer. This handles the error more gracefully. There is not much we can do yet about dealing with the error itself, and marking read is probably not important enough to warrant keeping a queue of pending changes or anything. However this commit changes it so that - we do try and make future mark read tag changes. - we display the tag state correctly: i.e. we don't display the tag as deleted (no strike through) - and since we know the tag change failed we can try to mark this message read in the future. Indeed, since the code uses the post-command hook we will try again on the next keypress (unless the user has left the message). We indicate to the user that these mark read tag changes may have failed in the header-line. --- emacs/notmuch-show.el | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index fea39faf..6d3149bf 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -1701,12 +1701,23 @@ user decision and we should not override it." (notmuch-show-mark-read) (notmuch-show-set-prop :seen t))) +(defvar notmuch-show--seen-has-errored nil) +(make-variable-buffer-local 'notmuch-show--seen-has-errored) + (defun notmuch-show-command-hook () (when (eq major-mode 'notmuch-show-mode) ;; We need to redisplay to get window-start and window-end correct. (redisplay) (save-excursion - (funcall notmuch-show-mark-read-function (window-start) (window-end))))) + (condition-case err + (funcall notmuch-show-mark-read-function (window-start) (window-end)) + ((debug error) + (unless notmuch-show--seen-has-errored + (setq notmuch-show--seen-has-errored 't) + (setq header-line-format + (concat header-line-format + (propertize " [some mark read tag changes may have failed]" + 'face font-lock-warning-face))))))))) (defun notmuch-show-filter-thread (query) "Filter or LIMIT the current thread based on a new query string. From be55acf4f0379b964e8c85cb20e2834b25842f4d Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Thu, 9 Jun 2016 22:20:56 +0300 Subject: [PATCH 052/179] test/notmuch-test: simple streamlining A few simple things that applies well to test/notmuch-test.sh - Shell does pathname expansion also without doing `echo ...` in subshell. - Redirections >/dev/null 2>/dev/null hide (improbable but) potential serious errors; adding -f to rm instead. - Inter-file capitalization consistency in comments. - Unnecesary space removal. --- test/notmuch-test | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/notmuch-test b/test/notmuch-test index 988dce6c..e7d3151c 100755 --- a/test/notmuch-test +++ b/test/notmuch-test @@ -17,14 +17,14 @@ fi cd "$(dirname "$0")" -TESTS=${NOTMUCH_TESTS:-`echo T[0-9][0-9][0-9]-*.sh`} +TESTS=${NOTMUCH_TESTS:-T[0-9][0-9][0-9]-*.sh} # Clean up any results from a previous run -rm -r test-results >/dev/null 2>/dev/null +rm -rf test-results -# test for timeout utility +# Test for timeout utility if command -v timeout >/dev/null; then - TEST_TIMEOUT_CMD="timeout 2m " + TEST_TIMEOUT_CMD="timeout 2m" echo "INFO: using 2 minute timeout for tests" else TEST_TIMEOUT_CMD="" From 38f0d44a82d4a6584bd95f611dfb429057a37b62 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Mon, 27 Jun 2016 20:38:40 +0200 Subject: [PATCH 053/179] doc: forbid further operations on a closed database We could add many null pointer checks, but currently I don't see a use case that justifies it. --- lib/notmuch.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/notmuch.h b/lib/notmuch.h index d4a97cb8..2faa1468 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -332,7 +332,9 @@ notmuch_database_status_string (const notmuch_database_t *notmuch); * functions on objects derived from this database may either behave * as if the database had not been closed (e.g., if the required data * has been cached) or may fail with a - * NOTMUCH_STATUS_XAPIAN_EXCEPTION. + * NOTMUCH_STATUS_XAPIAN_EXCEPTION. The only further operation + * permitted on the database itself is to call + * notmuch_database_destroy. * * notmuch_database_close can be called multiple times. Later calls * have no effect. From 496dccf44fadf4c8ee190e98d3cba54a8e828f97 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 28 Jun 2016 10:24:07 +0200 Subject: [PATCH 054/179] test: don't use dump and restore in a pipeline This has been wrong since bbbdf0478ea, but the race condition was not previously been (often?) triggered in the tests. With the DB_RETRY_LOCK patches, it manifests itself as a deadlock. --- test/T240-dump-restore.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/T240-dump-restore.sh b/test/T240-dump-restore.sh index bbfb09b9..faa10364 100755 --- a/test/T240-dump-restore.sh +++ b/test/T240-dump-restore.sh @@ -135,7 +135,8 @@ test_expect_equal_file EXPECTED OUT test_begin_subtest "format=batch-tag, # round-trip" notmuch dump --format=sup | sort > EXPECTED.$test_count -notmuch dump --format=batch-tag | notmuch restore --format=batch-tag +notmuch dump --format=batch-tag > DUMPFILE +notmuch restore --format=batch-tag < DUMPFILE notmuch dump --format=sup | sort > OUTPUT.$test_count test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count @@ -212,7 +213,8 @@ test_expect_equal_file EXPECTED OUTPUT.$test_count test_begin_subtest 'format=batch-tag, round trip with strange tags' notmuch dump --format=batch-tag > EXPECTED.$test_count -notmuch dump --format=batch-tag | notmuch restore --format=batch-tag +notmuch dump --format=batch-tag > DUMPFILE +notmuch restore --format=batch-tag < DUMPFILE notmuch dump --format=batch-tag > OUTPUT.$test_count test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count From 9b60dc3cd9224eddfe9bc5cf74eeaa5f0d599d04 Mon Sep 17 00:00:00 2001 From: Istvan Marko Date: Sun, 26 Jun 2016 17:29:43 +0200 Subject: [PATCH 055/179] Use the Xapian::DB_RETRY_LOCK flag when available Xapian 1.3 has introduced the DB_RETRY_LOCK flag (Xapian bug 275). Detect it in configure and optionally use it. With this flag commands that need the write lock will wait for their turn instead of aborting when it's not immediately available. Amended by db: allow disabling in configure --- configure | 35 +++++++++++++++++++++++++++++++++++ lib/database.cc | 8 +++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 49fa5b93..ae0a0270 100755 --- a/configure +++ b/configure @@ -72,6 +72,7 @@ WITH_EMACS=1 WITH_BASH=1 WITH_RUBY=1 WITH_ZSH=1 +WITH_RETRY_LOCK=1 usage () { @@ -140,6 +141,7 @@ Some features can be disabled (--with-feature=no is equivalent to --without-emacs Do not install lisp file --without-ruby Do not install ruby bindings --without-zsh-completion Do not install zsh completions files + --without-retry-lock Do not use blocking xapian opens, even if available Additional options are accepted for compatibility with other configure-script calling conventions, but don't do anything yet: @@ -211,6 +213,14 @@ for option; do fi elif [ "${option}" = '--without-ruby' ] ; then WITH_RUBY=0 + elif [ "${option%%=*}" = '--with-retry-lock' ]; then + if [ "${option#*=}" = 'no' ]; then + WITH_RETRY_LOCK=0 + else + WITH_RETRY_LOCK=1 + fi + elif [ "${option}" = '--without-retry-lock' ] ; then + WITH_RETRY_LOCK=0 elif [ "${option%%=*}" = '--with-zsh-completion' ]; then if [ "${option#*=}" = 'no' ]; then WITH_ZSH=0 @@ -392,6 +402,24 @@ EOF rm -f _field_processor.o _field_processor.cc default_xapian_backend="" + # DB_RETRY_LOCK is only supported on Xapian > 1.3.2 + have_xapian_db_retry_lock=0 + if [ $WITH_RETRY_LOCK = "1" ]; then + printf "Checking for Xapian lock retry support... " + cat>_retry.cc< +int flag = Xapian::DB_RETRY_LOCK; +EOF + if ${CXX} ${CXXFLAGS_for_sh} ${xapian_cxxflags} -c _retry.cc -o _retry.o > /dev/null 2>&1 + then + have_xapian_db_retry_lock=1 + printf "Yes.\n" + else + printf "No. (optional)\n" + fi + rm -f _retry.o _retry.cc + fi + printf "Testing default Xapian backend... " cat >_default_backend.cc < @@ -1022,6 +1050,9 @@ HAVE_XAPIAN_COMPACT = ${have_xapian_compact} # Whether the Xapian version in use supports field processors HAVE_XAPIAN_FIELD_PROCESSOR = ${have_xapian_field_processor} +# Whether the Xapian version in use supports DB_RETRY_LOCK +HAVE_XAPIAN_DB_RETRY_LOCK = ${have_xapian_db_retry_lock} + # Whether the getpwuid_r function is standards-compliant # (if not, then notmuch will #define _POSIX_PTHREAD_SEMANTICS # to enable the standards-compliant version -- needed for Solaris) @@ -1097,6 +1128,7 @@ COMMON_CONFIGURE_CFLAGS = \\ -DSTD_ASCTIME=\$(STD_ASCTIME) \\ -DHAVE_XAPIAN_COMPACT=\$(HAVE_XAPIAN_COMPACT) \\ -DHAVE_XAPIAN_FIELD_PROCESSOR=\$(HAVE_XAPIAN_FIELD_PROCESSOR) \\ + -DHAVE_XAPIAN_DB_RETRY_LOCK=\$(HAVE_XAPIAN_DB_RETRY_LOCK) \\ -DUTIL_BYTE_ORDER=\$(UTIL_BYTE_ORDER) CONFIGURE_CFLAGS = \$(COMMON_CONFIGURE_CFLAGS) @@ -1117,6 +1149,9 @@ NOTMUCH_HAVE_XAPIAN_COMPACT=${have_xapian_compact} # Whether the Xapian version in use supports field processors NOTMUCH_HAVE_XAPIAN_FIELD_PROCESSOR=${have_xapian_field_processor} +# Whether the Xapian version in use supports lock retry +NOTMUCH_HAVE_XAPIAN_DB_RETRY_LOCK=${have_xapian_db_retry_lock} + # Which backend will Xapian use by default? NOTMUCH_DEFAULT_XAPIAN_BACKEND=${default_xapian_backend} diff --git a/lib/database.cc b/lib/database.cc index afafe88c..66ee267f 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -49,6 +49,12 @@ typedef struct { #define STRINGIFY(s) _SUB_STRINGIFY(s) #define _SUB_STRINGIFY(s) #s +#if HAVE_XAPIAN_DB_RETRY_LOCK +#define DB_ACTION (Xapian::DB_CREATE_OR_OPEN | Xapian::DB_RETRY_LOCK) +#else +#define DB_ACTION Xapian::DB_CREATE_OR_OPEN +#endif + /* Here's the current schema for our database (for NOTMUCH_DATABASE_VERSION): * * We currently have three different types of documents (mail, ghost, @@ -939,7 +945,7 @@ notmuch_database_open_verbose (const char *path, if (mode == NOTMUCH_DATABASE_MODE_READ_WRITE) { notmuch->xapian_db = new Xapian::WritableDatabase (xapian_path, - Xapian::DB_CREATE_OR_OPEN); + DB_ACTION); } else { notmuch->xapian_db = new Xapian::Database (xapian_path); } From 6450813e8f211c87e9a2b48c76fb0c99c57410d2 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 26 Jun 2016 17:29:44 +0200 Subject: [PATCH 056/179] test: initial tests for locking retry Currently there's not much to test, so we simulate contention, and check that the modifications to the database are serialized. --- test/T620-lock.sh | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100755 test/T620-lock.sh diff --git a/test/T620-lock.sh b/test/T620-lock.sh new file mode 100755 index 00000000..f46475e8 --- /dev/null +++ b/test/T620-lock.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +test_description="locking" +. ./test-lib.sh || exit 1 + +if [ "${NOTMUCH_HAVE_XAPIAN_DB_RETRY_LOCK}" = "0" ]; then + test_subtest_missing_external_prereq_["lock retry support"]=t +fi + +add_email_corpus + +test_begin_subtest "blocking open" +test_C ${MAIL_DIR} <<'EOF' +#include +#include +#include +#include + +void +taggit (notmuch_database_t *db, const char *tag) +{ + notmuch_message_t *message; + + EXPECT0 (notmuch_database_find_message (db, "4EFC743A.3060609@april.org", &message)); + if (message == NULL) { + fprintf (stderr, "unable to find message"); + exit (1); + } + + EXPECT0 (notmuch_message_add_tag (message, tag)); + notmuch_message_destroy (message); +} + +int +main (int argc, char **argv) +{ + pid_t child; + const char *path = argv[1]; + + child = fork (); + if (child == -1) { + fprintf (stderr, "fork failed\n"); + exit (1); + } + + if (child == 0) { + notmuch_database_t *db2; + + sleep (1); + EXPECT0 (notmuch_database_open (path, NOTMUCH_DATABASE_MODE_READ_WRITE, &db2)); + taggit (db2, "child"); + EXPECT0 (notmuch_database_close (db2)); + } else { + notmuch_database_t *db; + + EXPECT0 (notmuch_database_open (path, NOTMUCH_DATABASE_MODE_READ_WRITE, &db)); + taggit (db, "parent"); + sleep (2); + EXPECT0 (notmuch_database_close (db)); + wait (NULL); + } +} + +EOF +notmuch search --output=tags id:4EFC743A.3060609@april.org >> OUTPUT +cat <<'EOF' >EXPECTED +== stdout == +== stderr == +child +inbox +parent +unread +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_done From 3a45d29ed4d753bcf72f2fa0bb37fefd4d18f96a Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 26 Jun 2016 17:29:45 +0200 Subject: [PATCH 057/179] lib: add built_with handling for XAPIAN_DB_RETRY_LOCK This support will be present only if the appropriate version of xapian is available _and_ the user did not disable the feature when building. So there really needs to be some way for the user to check. --- lib/built-with.c | 2 ++ notmuch-config.c | 3 +++ test/T030-config.sh | 1 + test/T040-setup.sh | 3 ++- 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/built-with.c b/lib/built-with.c index 635ed3b3..2f1f0b5c 100644 --- a/lib/built-with.c +++ b/lib/built-with.c @@ -28,6 +28,8 @@ notmuch_built_with (const char *name) return HAVE_XAPIAN_COMPACT; } else if (STRNCMP_LITERAL (name, "field_processor") == 0) { return HAVE_XAPIAN_FIELD_PROCESSOR; + } else if (STRNCMP_LITERAL (name, "retry_lock") == 0) { + return HAVE_XAPIAN_DB_RETRY_LOCK; } else { return FALSE; } diff --git a/notmuch-config.c b/notmuch-config.c index de9a8a41..e5d42a0c 100644 --- a/notmuch-config.c +++ b/notmuch-config.c @@ -911,6 +911,9 @@ _notmuch_config_list_built_with () printf("%sfield_processor=%s\n", BUILT_WITH_PREFIX, notmuch_built_with ("field_processor") ? "true" : "false"); + printf("%sretry_lock=%s\n", + BUILT_WITH_PREFIX, + notmuch_built_with ("retry_lock") ? "true" : "false"); } static int diff --git a/test/T030-config.sh b/test/T030-config.sh index b8d5a86f..0915abdb 100755 --- a/test/T030-config.sh +++ b/test/T030-config.sh @@ -59,6 +59,7 @@ foo.string=this is another string value foo.list=this;is another;list value; built_with.compact=something built_with.field_processor=something +built_with.retry_lock=something EOF test_expect_equal_file EXPECTED OUTPUT diff --git a/test/T040-setup.sh b/test/T040-setup.sh index be2f0dbd..021f2d0b 100755 --- a/test/T040-setup.sh +++ b/test/T040-setup.sh @@ -31,6 +31,7 @@ search.exclude_tags=baz; maildir.synchronize_flags=true crypto.gpg_path=gpg built_with.compact=something -built_with.field_processor=something" +built_with.field_processor=something +built_with.retry_lock=something" test_done From 48d33532bb58c2ff61d687011dc0283e3ff536b0 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 28 Jun 2016 10:24:07 +0200 Subject: [PATCH 058/179] test: don't use dump and restore in a pipeline This has been wrong since bbbdf0478ea, but the race condition was not previously been (often?) triggered in the tests. With the DB_RETRY_LOCK patches, it manifests itself as a deadlock. --- test/T240-dump-restore.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/T240-dump-restore.sh b/test/T240-dump-restore.sh index e6976ff8..97e9e7f9 100755 --- a/test/T240-dump-restore.sh +++ b/test/T240-dump-restore.sh @@ -135,7 +135,8 @@ test_expect_equal_file EXPECTED OUT test_begin_subtest "format=batch-tag, # round-trip" notmuch dump --format=sup | sort > EXPECTED.$test_count -notmuch dump --format=batch-tag | notmuch restore --format=batch-tag +notmuch dump --format=batch-tag > DUMPFILE +notmuch restore --format=batch-tag < DUMPFILE notmuch dump --format=sup | sort > OUTPUT.$test_count test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count @@ -212,7 +213,8 @@ test_expect_equal_file EXPECTED OUTPUT.$test_count test_begin_subtest 'format=batch-tag, round trip with strange tags' notmuch dump --format=batch-tag > EXPECTED.$test_count -notmuch dump --format=batch-tag | notmuch restore --format=batch-tag +notmuch dump --format=batch-tag > DUMPFILE +notmuch restore --format=batch-tag < DUMPFILE notmuch dump --format=batch-tag > OUTPUT.$test_count test_expect_equal_file EXPECTED.$test_count OUTPUT.$test_count From f2886b80c1addc3d358ba910a4eebf76ba74c00b Mon Sep 17 00:00:00 2001 From: David Bremner Date: Mon, 27 Jun 2016 20:38:40 +0200 Subject: [PATCH 059/179] doc: forbid further operations on a closed database We could add many null pointer checks, but currently I don't see a use case that justifies it. --- lib/notmuch.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/notmuch.h b/lib/notmuch.h index cb46fc05..a43e167c 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -331,7 +331,9 @@ notmuch_database_status_string (const notmuch_database_t *notmuch); * functions on objects derived from this database may either behave * as if the database had not been closed (e.g., if the required data * has been cached) or may fail with a - * NOTMUCH_STATUS_XAPIAN_EXCEPTION. + * NOTMUCH_STATUS_XAPIAN_EXCEPTION. The only further operation + * permitted on the database itself is to call + * notmuch_database_destroy. * * notmuch_database_close can be called multiple times. Later calls * have no effect. From ff9284db6ebec1e4cbd7528d63da3cdf4ea9ddff Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 7 Jun 2016 07:37:57 -0300 Subject: [PATCH 060/179] lib: fix definition of LIBNOTMUCH_CHECK_VERSION Fix bug reported in id:20160606124522.g2y2eazhhrwjsa4h@flatcap.org Although the C99 standard 6.10 is a little non-obvious on this point, the docs for e.g. gcc are unambiguous. And indeed in practice with the extra space, this code fails #include #define foo (x) (x+1) int main(int argc, char **argv){ printf("%d\n",foo(1)); } --- lib/notmuch.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/notmuch.h b/lib/notmuch.h index a43e167c..24944f0b 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -93,7 +93,7 @@ NOTMUCH_BEGIN_DECLS * #endif * @endcode */ -#define LIBNOTMUCH_CHECK_VERSION (major, minor, micro) \ +#define LIBNOTMUCH_CHECK_VERSION(major, minor, micro) \ (LIBNOTMUCH_MAJOR_VERSION > (major) || \ (LIBNOTMUCH_MAJOR_VERSION == (major) && LIBNOTMUCH_MINOR_VERSION > (minor)) || \ (LIBNOTMUCH_MAJOR_VERSION == (major) && LIBNOTMUCH_MINOR_VERSION == (minor) && \ From 9e6017b8b48db2268e7fc772c6009935838cc7e4 Mon Sep 17 00:00:00 2001 From: Stefano Zacchiroli Date: Thu, 21 Apr 2016 14:10:25 -0400 Subject: [PATCH 061/179] notmuch-mutt: use env to locate perl for increased portability Note: this patch drops -w from the shebang line, but we still have "use warnings" in the script, which is superior anyhow. Thanks Andreas Tolfsen for the suggestion. --- contrib/notmuch-mutt/notmuch-mutt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/notmuch-mutt/notmuch-mutt b/contrib/notmuch-mutt/notmuch-mutt index b47b3651..97fb2d85 100755 --- a/contrib/notmuch-mutt/notmuch-mutt +++ b/contrib/notmuch-mutt/notmuch-mutt @@ -1,4 +1,4 @@ -#!/usr/bin/perl -w +#!/usr/bin/env perl # # notmuch-mutt - notmuch (of a) helper for Mutt # From 308b44290d8b2bfc0196964bb860d6a5c0781073 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Thu, 30 Jun 2016 17:52:42 +0200 Subject: [PATCH 062/179] version: bump to 0.21.1~rc0 Getting ready for the point release --- bindings/python/notmuch/version.py | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/python/notmuch/version.py b/bindings/python/notmuch/version.py index c1d472be..df5794f6 100644 --- a/bindings/python/notmuch/version.py +++ b/bindings/python/notmuch/version.py @@ -1,3 +1,3 @@ # this file should be kept in sync with ../../../version -__VERSION__ = '0.22' +__VERSION__ = '0.22.1~rc0' SOVERSION = '4' diff --git a/version b/version index e3462940..6f56c09b 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.22 +0.22.1~rc0 From 09b18ae8f459ccdb1e5706818b4bf45ceea65e23 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Thu, 30 Jun 2016 21:28:31 +0200 Subject: [PATCH 063/179] debian: add changelog stanza for 0.22.1~rc0-1 No actual changes discussed, because the NEWS file is yet to be created. --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index fdb14edd..1a2a494f 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +notmuch (0.22.1~rc0-1) experimental; urgency=medium + + * release candidate for bugfix release + + -- David Bremner Thu, 30 Jun 2016 21:28:13 +0200 + notmuch (0.22-1) unstable; urgency=medium * New upstream release. See /usr/share/doc/notmuch/NEWS for new From 713f7d0c0c2859e4e5d84d2564c7ec1bd506c73c Mon Sep 17 00:00:00 2001 From: David Bremner Date: Wed, 13 Jul 2016 19:48:13 -0300 Subject: [PATCH 064/179] NEWS: initial NEWS for 0.22.1 --- NEWS | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/NEWS b/NEWS index c945c245..0e8c37e0 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,34 @@ +Notmuch 0.22.1 (UNRELEASED) +=========================== + +Library +------- + +Correct the definition of `LIBNOTMUCH_CHECK_VERSION`. + +Document the (lack of) operations permitted on a closed database. + +Test Suite +---------- + +Fix race condition in dump / restore tests. + +Notmuch-Mutt +------------ + +Use `env` to locate perl. + +Emacs +----- + +Tell `message-mode` mode that outgoing messages are mail + + This makes message-mode configuration behave more predictably. + +Respect charset of MIME parts when reading them + + Fix previous assumption that everyone uses UTF-8. + Notmuch 0.22 (2016-04-26) ========================= From 51816247d2eacf0af3367c0db85d710a7788797b Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 19 Jul 2016 06:33:37 -0300 Subject: [PATCH 065/179] version: bump to 0.22 --- bindings/python/notmuch/version.py | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/python/notmuch/version.py b/bindings/python/notmuch/version.py index df5794f6..0dcbb4e3 100644 --- a/bindings/python/notmuch/version.py +++ b/bindings/python/notmuch/version.py @@ -1,3 +1,3 @@ # this file should be kept in sync with ../../../version -__VERSION__ = '0.22.1~rc0' +__VERSION__ = '0.22.1' SOVERSION = '4' diff --git a/version b/version index 6f56c09b..a723ece7 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.22.1~rc0 +0.22.1 From 2d2d00108151873800deb5a62cdf9d93fb188b14 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 19 Jul 2016 06:36:15 -0300 Subject: [PATCH 066/179] NEWS: set date of point release --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 0e8c37e0..3db6aba9 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -Notmuch 0.22.1 (UNRELEASED) +Notmuch 0.22.1 (2016-07-19) =========================== Library From b824ecc48f1e838ab19e346b0eec17ff02432493 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 19 Jul 2016 06:43:20 -0300 Subject: [PATCH 067/179] debian: changelog stanza for 0.22.1-1 Basically copy upstream NEWS --- debian/changelog | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/debian/changelog b/debian/changelog index 1a2a494f..916100d9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,14 @@ +notmuch (0.22.1-1) unstable; urgency=medium + + * Correct the definition of `LIBNOTMUCH_CHECK_VERSION`. + * Document the (lack of) operations permitted on a closed database + (Closes: #826843). + * Fix race condition in dump / restore tests. + * [notmuch-emacs] Tell `message-mode` mode that outgoing messages are mail + * [notmuch-emacs] Respect charset of MIME parts when reading them + + -- David Bremner Tue, 19 Jul 2016 06:42:09 -0300 + notmuch (0.22.1~rc0-1) experimental; urgency=medium * release candidate for bugfix release From 6cf6469e91938afafd22c34773168436a63ca47c Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 19 Jul 2016 08:47:47 -0300 Subject: [PATCH 068/179] debian: add explicit build-depends on gnupg apt dropped it's depends on gnupg, so we can't rely on it being present in the build env anymore --- debian/control | 1 + 1 file changed, 1 insertion(+) diff --git a/debian/control b/debian/control index a46e391b..2cd24213 100644 --- a/debian/control +++ b/debian/control @@ -24,6 +24,7 @@ Build-Depends: gdb [!s390x !ia64 !armel !ppc64el !mips !mipsel !mips64el], dtach (>= 0.8), gpgsm , + gnupg , bash-completion (>=1.9.0~) Standards-Version: 3.9.6 Homepage: http://notmuchmail.org/ From 8fe58226ef81e9600c97675e62d4a78e0f6eeefe Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 19 Jul 2016 08:51:33 -0300 Subject: [PATCH 069/179] debian: changelog stanza for 0.22.1-2 --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index 916100d9..bac4fcb9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +notmuch (0.22.1-2) unstable; urgency=medium + + * Add explicit build-depends on gnupg, for the test suite. + + -- David Bremner Tue, 19 Jul 2016 08:50:13 -0300 + notmuch (0.22.1-1) unstable; urgency=medium * Correct the definition of `LIBNOTMUCH_CHECK_VERSION`. From 4bf82dea2d84ffeb17eb335ed97fca9496cae7f1 Mon Sep 17 00:00:00 2001 From: Jaime Velasco Juan Date: Mon, 4 Jul 2016 19:49:31 +0100 Subject: [PATCH 070/179] Fix typo in Message.maildir_flags_to_tags It has been calling _tags_to_maildir_flags instead since the beginning, --- bindings/python/notmuch/message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py index bf809008..fc177eb2 100644 --- a/bindings/python/notmuch/message.py +++ b/bindings/python/notmuch/message.py @@ -572,7 +572,7 @@ class Message(Python3StringMixIn): notmuch.STATUS.SUCCESS here. See there for details.""" if not self._msg: raise NotInitializedError() - return Message._tags_to_maildir_flags(self._msg) + return Message._maildir_flags_to_tags(self._msg) def __repr__(self): """Represent a Message() object by str()""" From 2ce0f1368f3c69071c04019c6aa8b6dfcca2015b Mon Sep 17 00:00:00 2001 From: David Bremner Date: Fri, 22 Jul 2016 09:47:34 -0300 Subject: [PATCH 071/179] create .mailmap file (for git shortlog/blame) Recently it was suggested on the list that we add a contributor to AUTHORS. This file has been unmodified for 7 years, and is somewhat out of date. It would potentially make sense to automagically update this file during the release process using the output from git shortlog. This file helps clean up some inconsistencies in author data in the history. --- .mailmap | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .mailmap diff --git a/.mailmap b/.mailmap new file mode 100644 index 00000000..935c6eb0 --- /dev/null +++ b/.mailmap @@ -0,0 +1,8 @@ +Peter Feigl +Nate +Ali Polatel +Stefan +Patrick Totzke +Patrick Totzke +Patrick Totzke +Mark Walters From 2a7b11b064233afc4feead876fa396e3c18a6b91 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Thu, 21 Jul 2016 14:30:33 -0700 Subject: [PATCH 072/179] emacs: express n-search-line-faces in terms of two new faces The two new faces (notmuch-search-flagged-face and notmuch-search-unread-face) make it easier to find the relevant face by customizing notmuch-faces. I plan to do the same to the other alists of faces found elsewhere. --- NEWS | 10 ++++++++++ emacs/notmuch.el | 39 ++++++++++++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/NEWS b/NEWS index ab661d4d..3a9c8d36 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,16 @@ Notmuch 0.23 (UNRELEASED) ========================= +Emacs +----- + +Face customization is easier + + New faces `notmuch-search-flagged-face` and + `notmuch-search-unread-face` are used by default by + `notmuch-search-line-faces`. Customize `notmuch-faces` to modify + them. + Ruby Bindings ------------- diff --git a/emacs/notmuch.el b/emacs/notmuch.el index 8acdef37..43d56f7b 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -311,6 +311,26 @@ there will be called at other points of notmuch execution." :group 'notmuch-search :group 'notmuch-faces) +(defface notmuch-search-flagged-face + '((t + (:weight bold))) + "Face used in search mode face for flagged threads. + +This face is the default value for the \"flagged\" tag in +`notmuch-search-line-faces`." + :group 'notmuch-search + :group 'notmuch-faces) + +(defface notmuch-search-unread-face + '((t + (:foreground "blue"))) + "Face used in search mode for unread threads. + +This face is the default value for the \"unread\" tag in +`notmuch-search-line-faces`." + :group 'notmuch-search + :group 'notmuch-faces) + (defun notmuch-search-mode () "Major mode displaying results of a notmuch search. @@ -654,9 +674,12 @@ of the result." (goto-char (point-min)) (forward-line (1- notmuch-search-target-line))))))))) -(defcustom notmuch-search-line-faces '(("unread" :weight bold) - ("flagged" :foreground "blue")) - "Tag/face mapping for line highlighting in notmuch-search. +(defcustom notmuch-search-line-faces + '(("unread" 'notmuch-search-unread-face) + ("flagged" 'notmuch-search-flagged-face)) + "Alist of tags to faces for line highlighting in notmuch-search. +Each element looks like (TAG . FACE). +A thread with TAG will have FACE applied. Here is an example of how to color search results based on tags. (the following text would be placed in your ~/.emacs file): @@ -665,10 +688,12 @@ Here is an example of how to color search results based on tags. (\"deleted\" . (:foreground \"red\" :background \"blue\")))) -The attributes defined for matching tags are merged, with earlier -attributes overriding later. A message having both \"deleted\" -and \"unread\" tags with the above settings would have a green -foreground and blue background." +The FACE must be a face name (a symbol or string), a property +list of face attributes, or a list of these. The faces for +matching tags are merged, with earlier attributes overriding +later. A message having both \"deleted\" and \"unread\" tags with +the above settings would have a green foreground and blue +background." :type '(alist :key-type (string) :value-type (custom-face-edit)) :group 'notmuch-search :group 'notmuch-faces) From f45fa5bdd397d52473f7092f7ae3e2ffb9b7aee5 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 28 Jun 2016 23:08:54 +0200 Subject: [PATCH 073/179] test: make gdb even quieter gdb sometimes writes warnings to stdout, which we don't need/want, and for some reason --batch-silent isn't enough to hide. So in this commit we write them to a log file, which is probably better for debugging anyway. To see an illustrative test failure before this change, run % make % touch notmuch-count.c % cd test && ./T060-count.sh --- test/T060-count.sh | 2 ++ test/T070-insert.sh | 2 ++ 2 files changed, 4 insertions(+) diff --git a/test/T060-count.sh b/test/T060-count.sh index 0ac83140..d6933a76 100755 --- a/test/T060-count.sh +++ b/test/T060-count.sh @@ -103,6 +103,8 @@ restore_database cat < count-files.gdb set breakpoint pending on +set logging file count-files-gdb.log +set logging on break count_files commands shell cp /dev/null ${MAIL_DIR}/.notmuch/xapian/postlist.${db_ending} diff --git a/test/T070-insert.sh b/test/T070-insert.sh index e7ec6a6c..c2485bb2 100755 --- a/test/T070-insert.sh +++ b/test/T070-insert.sh @@ -192,6 +192,8 @@ for code in OUT_OF_MEMORY XAPIAN_EXCEPTION FILE_NOT_EMAIL \ gen_insert_msg cat < index-file-$code.gdb set breakpoint pending on +set logging file index-file-$code.log +set logging on break notmuch_database_add_message commands return NOTMUCH_STATUS_$code From 293186d6c6b1c4d158413de5412e6b4345e94970 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Fri, 15 Jul 2016 07:25:41 -0300 Subject: [PATCH 074/179] lib: provide _notmuch_database_log_append _notmuch_database_log clears the log buffer each time. Rather than introducing more complicated semantics about for this function, provide a second function that does not clear the buffer. This is mainly a convenience function for callers constructing complex or multi-line log messages. The changes to query.cc are to make sure that the common code path of the new function is tested. --- lib/database.cc | 16 ++++++++++++++++ lib/notmuch-private.h | 4 ++++ lib/query.cc | 14 ++++++++------ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/database.cc b/lib/database.cc index 66ee267f..57a98c94 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -383,6 +383,22 @@ _notmuch_database_log (notmuch_database_t *notmuch, talloc_free (notmuch->status_string); notmuch->status_string = talloc_vasprintf (notmuch, format, va_args); + va_end (va_args); +} + +void +_notmuch_database_log_append (notmuch_database_t *notmuch, + const char *format, + ...) +{ + va_list va_args; + + va_start (va_args, format); + + if (notmuch->status_string) + notmuch->status_string = talloc_vasprintf_append (notmuch->status_string, format, va_args); + else + notmuch->status_string = talloc_vasprintf (notmuch, format, va_args); va_end (va_args); } diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 3721431e..643d9dd9 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -196,6 +196,10 @@ void _notmuch_database_log (notmuch_database_t *notmuch, const char *format, ...); +void +_notmuch_database_log_append (notmuch_database_t *notmuch, + const char *format, ...); + unsigned long _notmuch_database_new_revision (notmuch_database_t *notmuch); diff --git a/lib/query.cc b/lib/query.cc index 7eb73a13..53efd4e1 100644 --- a/lib/query.cc +++ b/lib/query.cc @@ -299,9 +299,10 @@ _notmuch_query_search_documents (notmuch_query_t *query, } catch (const Xapian::Error &error) { _notmuch_database_log (notmuch, - "A Xapian exception occurred performing query: %s\n" + "A Xapian exception occurred performing query: %s\n", + error.get_msg().c_str()); + _notmuch_database_log_append (notmuch, "Query string was: %s\n", - error.get_msg().c_str(), query->query_string); notmuch->exception_reported = TRUE; @@ -613,10 +614,11 @@ _notmuch_query_count_documents (notmuch_query_t *query, const char *type, unsign } catch (const Xapian::Error &error) { _notmuch_database_log (notmuch, - "A Xapian exception occurred performing query: %s\n" - "Query string was: %s\n", - error.get_msg().c_str(), - query->query_string); + "A Xapian exception occurred performing query: %s\n", + error.get_msg().c_str()); + _notmuch_database_log_append (notmuch, + "Query string was: %s\n", + query->query_string); return NOTMUCH_STATUS_XAPIAN_EXCEPTION; } From 999d473299781cb2a38fba5d9e2452504799a7a2 Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Sat, 6 Aug 2016 16:29:33 +0100 Subject: [PATCH 075/179] emacs: wash: word-wrap bugfix Previously notmuch-wash made the width of the text (approximately) the window-width minus the depth in thread. This is correct for the default indentation of 1 per message depth, but is incorrect for any other setting of notmuch-show-indent-messages-width. As notmuch-show-indent-messages-width is customisable, and notmuch-tree sets it to zero to avoid indenting messages in the message pane, this bug can show up in real use. Two of the tests had to be updated: when notmuch-show-indent-messages-width is 0, then the new (correct) word wrapping happens later, when notmuch-show-indent-messages-width is 4, then the new word wrapping happens sooner. --- emacs/notmuch-wash.el | 4 +++- ...-maildir-storage-with-fourfold-indentation | 19 ++++++++++++------- ...thread-maildir-storage-without-indentation | 8 ++++---- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/emacs/notmuch-wash.el b/emacs/notmuch-wash.el index 57e6dfa3..07fc1a1f 100644 --- a/emacs/notmuch-wash.el +++ b/emacs/notmuch-wash.el @@ -26,6 +26,7 @@ (require 'coolj) (declare-function notmuch-show-insert-bodypart "notmuch-show" (msg part depth &optional hide)) +(defvar notmuch-show-indent-messages-width) ;; @@ -335,12 +336,13 @@ message at the window width. When doing so, citation leaders in the wrapped text are maintained." (let* ((coolj-wrap-follows-window-size nil) + (indent (* depth notmuch-show-indent-messages-width)) (limit (if (numberp notmuch-wash-wrap-lines-length) (min notmuch-wash-wrap-lines-length (window-width)) (window-width))) (fill-column (- limit - depth + indent ;; 2 to avoid poor interaction with ;; `word-wrap'. 2))) diff --git a/test/emacs.expected-output/notmuch-show-thread-maildir-storage-with-fourfold-indentation b/test/emacs.expected-output/notmuch-show-thread-maildir-storage-with-fourfold-indentation index 4721b8b0..3bbb114a 100644 --- a/test/emacs.expected-output/notmuch-show-thread-maildir-storage-with-fourfold-indentation +++ b/test/emacs.expected-output/notmuch-show-thread-maildir-storage-with-fourfold-indentation @@ -109,12 +109,14 @@ http://notmuchmail.org/mailman/listinfo/notmuch To: notmuch@notmuchmail.org Date: Wed, 18 Nov 2009 02:50:48 +0600 - Twas brillig at 15:33:01 17.11.2009 UTC-05 when lars at seas.harvard.edu - did gyre and gimble: + Twas brillig at 15:33:01 17.11.2009 UTC-05 when lars at + seas.harvard.edu did gyre and gimble: LK> Is the list archived anywhere? The obvious archives - LK> (http://notmuchmail.org/pipermail/notmuch/) aren't available, and I - LK> think I subscribed too late to get the patch (I only just saw the + LK> (http://notmuchmail.org/pipermail/notmuch/) aren't available, + and I + LK> think I subscribed too late to get the patch (I only just saw + the LK> discussion about it). LK> It doesn't look like the patch is in git yet. @@ -141,7 +143,8 @@ http://notmuchmail.org/mailman/listinfo/notmuch seas.harvard.edu> wrote: > > See the patch just posted here. - I've also pushed a slightly more complicated (and complete) fix to my + I've also pushed a slightly more complicated (and complete) fix to + my private notmuch repository git://keithp.com/git/notmuch @@ -164,10 +167,12 @@ http://notmuchmail.org/mailman/listinfo/notmuch [ multipart/signed ] [ Unknown signature status ] [ text/plain ] - > I've also pushed a slightly more complicated (and complete) fix to my + > I've also pushed a slightly more complicated (and complete) + > fix to my > private notmuch repository - The version of lib/messages.cc in your repo doesn't build because it's + The version of lib/messages.cc in your repo doesn't build + because it's missing "#include " (for the uint32_t on line 466). [ 4-line signature. Click/Enter to show. ] diff --git a/test/emacs.expected-output/notmuch-show-thread-maildir-storage-without-indentation b/test/emacs.expected-output/notmuch-show-thread-maildir-storage-without-indentation index 62a46353..620caa00 100644 --- a/test/emacs.expected-output/notmuch-show-thread-maildir-storage-without-indentation +++ b/test/emacs.expected-output/notmuch-show-thread-maildir-storage-without-indentation @@ -49,8 +49,8 @@ Date: Wed, 18 Nov 2009 01:02:38 +0600 [ Unknown signature status ] [ text/plain ] -Twas brillig at 14:00:54 17.11.2009 UTC-05 when lars@seas.harvard.edu did -gyre and gimble: +Twas brillig at 14:00:54 17.11.2009 UTC-05 when lars@seas.harvard.edu did gyre +and gimble: LK> Resulted in 4604 lines of errors along the lines of: @@ -109,8 +109,8 @@ Subject: [notmuch] Working with Maildir storage? To: notmuch@notmuchmail.org Date: Wed, 18 Nov 2009 02:50:48 +0600 -Twas brillig at 15:33:01 17.11.2009 UTC-05 when lars at seas.harvard.edu -did gyre and gimble: +Twas brillig at 15:33:01 17.11.2009 UTC-05 when lars at seas.harvard.edu did +gyre and gimble: LK> Is the list archived anywhere? The obvious archives LK> (http://notmuchmail.org/pipermail/notmuch/) aren't available, and I From 188fccd84fc53f44f38a4b184383d63f2c58b5e3 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Wed, 10 Aug 2016 16:02:35 +0300 Subject: [PATCH 076/179] configure: add --without-api-docs option Add option to explicitly disable API man page build even if doxygen binary is available. --without-docs also implies not building API manpage. This change intended to add more distinctness into build system and allow user not to build unwanted man pages. --- configure | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/configure b/configure index ae0a0270..fa4c5b5e 100755 --- a/configure +++ b/configure @@ -68,6 +68,7 @@ PYTHON=${PYTHON:-} PREFIX=/usr/local LIBDIR= WITH_DOCS=1 +WITH_API_DOCS=1 WITH_EMACS=1 WITH_BASH=1 WITH_RUBY=1 @@ -137,7 +138,8 @@ Some features can be disabled (--with-feature=no is equivalent to --without-feature) : --without-bash-completion Do not install bash completions files - --without-docs Do not install documentation and man pages + --without-docs Do not install documentation + --without-api-docs Do not install API man page --without-emacs Do not install lisp file --without-ruby Do not install ruby bindings --without-zsh-completion Do not install zsh completions files @@ -184,11 +186,21 @@ for option; do elif [ "${option%%=*}" = '--with-docs' ]; then if [ "${option#*=}" = 'no' ]; then WITH_DOCS=0 + WITH_API_DOCS=0 else WITH_DOCS=1 fi elif [ "${option}" = '--without-docs' ] ; then WITH_DOCS=0 + WITH_API_DOCS=0 + elif [ "${option%%=*}" = '--with-api-docs' ]; then + if [ "${option#*=}" = 'no' ]; then + WITH_API_DOCS=0 + else + WITH_API_DOCS=1 + fi + elif [ "${option}" = '--without-api-docs' ] ; then + WITH_API_DOCS=0 elif [ "${option%%=*}" = '--with-emacs' ]; then if [ "${option#*=}" = 'no' ]; then WITH_EMACS=0 @@ -557,7 +569,7 @@ else fi have_doxygen=0 -if [ $WITH_DOCS = "1" ] ; then +if [ $WITH_API_DOCS = "1" ] ; then printf "Checking if doxygen is available... " if command -v doxygen > /dev/null; then printf "Yes.\n" From 1ba73d1437202b9ae3c2cff6d20dbe92fcacf053 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Mon, 8 Aug 2016 19:35:17 -0400 Subject: [PATCH 077/179] Omit User-Agent: header by default The User-Agent: header can be fun and interesting, but it also leaks quite a bit of information about the user and their software stack. This represents a potential security risk (attackers can target the particular stack) and also an anonymity risk (a user trying to preserve their anonymity by sending mail from a non-associated account might reveal quite a lot of information if their choice of mail user agent is exposed). This change also avoids hiding the User-Agent header by default, so that people who decide they want to send it will at least see it (and can edit it if they want to) before sending. It makes sense to have safer defaults. --- emacs/notmuch-mua.el | 4 ++-- test/T310-emacs.sh | 16 ---------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index 1ca80564..f3a4e5a3 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -62,7 +62,7 @@ disabled: this would result in an incorrect behavior.")) (const :tag "Compose mail in a new window" new-window) (const :tag "Compose mail in a new frame" new-frame))) -(defcustom notmuch-mua-user-agent-function 'notmuch-mua-user-agent-full +(defcustom notmuch-mua-user-agent-function nil "Function used to generate a `User-Agent:' string. If this is `nil' then no `User-Agent:' will be generated." :type '(choice (const :tag "No user agent string" nil) @@ -73,7 +73,7 @@ disabled: this would result in an incorrect behavior.")) :value notmuch-mua-user-agent-full)) :group 'notmuch-send) -(defcustom notmuch-mua-hidden-headers '("^User-Agent:") +(defcustom notmuch-mua-hidden-headers nil "Headers that are added to the `message-mode' hidden headers list." :type '(repeat string) diff --git a/test/T310-emacs.sh b/test/T310-emacs.sh index 65c1728d..202fc3bf 100755 --- a/test/T310-emacs.sh +++ b/test/T310-emacs.sh @@ -193,7 +193,6 @@ emacs_deliver_message \ (kill-whole-line) (insert "To: user@example.com\n")' sed \ - -e s',^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' \ -e s',^Message-ID: <.*>$,Message-ID: ,' \ -e s',^\(Content-Type: text/plain\); charset=us-ascii$,\1,' < sent_message >OUTPUT cat <EXPECTED @@ -201,7 +200,6 @@ From: Notmuch Test Suite To: user@example.com Subject: Testing message sent via SMTP Date: 01 Jan 2000 12:00:00 -0000 -User-Agent: Notmuch/XXX Emacs/XXX Message-ID: MIME-Version: 1.0 Content-Type: text/plain @@ -310,7 +308,6 @@ test_emacs '(let ((message-hidden-headers ''())) (test-output))' sed -i -e 's/^In-Reply-To: <.*>$/In-Reply-To: /' OUTPUT sed -i -e 's/^References: <.*>$/References: /' OUTPUT -sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT cat <EXPECTED From: Notmuch Test Suite To: user@example.com @@ -318,7 +315,6 @@ Subject: Re: Testing message sent via SMTP In-Reply-To: Fcc: ${MAIL_DIR}/sent References: -User-Agent: Notmuch/XXX Emacs/XXX --text follows this line-- Notmuch Test Suite writes: @@ -335,7 +331,6 @@ test_emacs "(let ((message-hidden-headers '())) (notmuch-test-wait) (notmuch-search-reply-to-thread) (test-output))" -sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT cat <EXPECTED From: Notmuch Test Suite To: Sender @@ -343,7 +338,6 @@ Subject: Re: ${test_subtest_name} In-Reply-To: <${gen_msg_id}> Fcc: ${MAIL_DIR}/sent References: <${gen_msg_id}> -User-Agent: Notmuch/XXX Emacs/XXX --text follows this line-- Sender writes: @@ -361,7 +355,6 @@ test_emacs "(let ((message-hidden-headers '())) (notmuch-test-wait) (notmuch-search-reply-to-thread) (test-output))" -sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT cat <EXPECTED From: Notmuch Test Suite To: Sender , someone@example.com @@ -369,7 +362,6 @@ Subject: Re: ${test_subtest_name} In-Reply-To: <${gen_msg_id}> Fcc: ${MAIL_DIR}/sent References: <${gen_msg_id}> -User-Agent: Notmuch/XXX Emacs/XXX --text follows this line-- Sender writes: @@ -382,7 +374,6 @@ test_emacs '(let ((message-hidden-headers ''())) (notmuch-show "id:20091118002059.067214ed@hikari") (notmuch-show-reply) (test-output))' -sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT cat <EXPECTED From: Notmuch Test Suite To: Adrian Perez de Castro , notmuch@notmuchmail.org @@ -390,7 +381,6 @@ Subject: Re: [notmuch] Introducing myself In-Reply-To: <20091118002059.067214ed@hikari> Fcc: ${MAIL_DIR}/sent References: <20091118002059.067214ed@hikari> -User-Agent: Notmuch/XXX Emacs/XXX --text follows this line-- Adrian Perez de Castro writes: @@ -447,7 +437,6 @@ test_emacs '(let ((message-hidden-headers ''())) (notmuch-show "id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com") (notmuch-show-reply) (test-output))' -sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT cat <EXPECTED From: Notmuch Test Suite To: Alex Botero-Lowry , notmuch@notmuchmail.org @@ -455,7 +444,6 @@ Subject: Re: [notmuch] preliminary FreeBSD support In-Reply-To: Fcc: ${MAIL_DIR}/sent References: -User-Agent: Notmuch/XXX Emacs/XXX --text follows this line-- Alex Botero-Lowry writes: @@ -521,7 +509,6 @@ test_emacs "(let ((message-hidden-headers '())) (notmuch-show \"id:${gen_msg_id}\") (notmuch-show-reply) (test-output))" -sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT cat <EXPECTED From: Notmuch Test Suite To: @@ -529,7 +516,6 @@ Subject: Re: Reply within emacs to an html-only message In-Reply-To: <${gen_msg_id}> Fcc: ${MAIL_DIR}/sent References: <${gen_msg_id}> -User-Agent: Notmuch/XXX Emacs/XXX --text follows this line-- Notmuch Test Suite writes: @@ -546,7 +532,6 @@ test_emacs "(let ((message-hidden-headers '())) (notmuch-show \"id:$message_id\") (notmuch-show-reply) (test-output))" -sed -i -e 's,^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' OUTPUT cat <EXPECTED From: Notmuch Test Suite To: @@ -554,7 +539,6 @@ Subject: Re: Quote MML tags in reply In-Reply-To: Fcc: ${MAIL_DIR}/sent References: -User-Agent: Notmuch/XXX Emacs/XXX --text follows this line-- Notmuch Test Suite writes: From cf8aabdd3759519ad8c56852fe03908b1b09bc03 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 28 Jun 2016 23:08:54 +0200 Subject: [PATCH 078/179] test: make gdb even quieter gdb sometimes writes warnings to stdout, which we don't need/want, and for some reason --batch-silent isn't enough to hide. So in this commit we write them to a log file, which is probably better for debugging anyway. To see an illustrative test failure before this change, run % make % touch notmuch-count.c % cd test && ./T060-count.sh (cherry picked from commit f45fa5bdd397d52473f7092f7ae3e2ffb9b7aee5) --- test/T060-count.sh | 2 ++ test/T070-insert.sh | 2 ++ 2 files changed, 4 insertions(+) diff --git a/test/T060-count.sh b/test/T060-count.sh index 0ac83140..d6933a76 100755 --- a/test/T060-count.sh +++ b/test/T060-count.sh @@ -103,6 +103,8 @@ restore_database cat < count-files.gdb set breakpoint pending on +set logging file count-files-gdb.log +set logging on break count_files commands shell cp /dev/null ${MAIL_DIR}/.notmuch/xapian/postlist.${db_ending} diff --git a/test/T070-insert.sh b/test/T070-insert.sh index e7ec6a6c..c2485bb2 100755 --- a/test/T070-insert.sh +++ b/test/T070-insert.sh @@ -192,6 +192,8 @@ for code in OUT_OF_MEMORY XAPIAN_EXCEPTION FILE_NOT_EMAIL \ gen_insert_msg cat < index-file-$code.gdb set breakpoint pending on +set logging file index-file-$code.log +set logging on break notmuch_database_add_message commands return NOTMUCH_STATUS_$code From 98da3c079a6b42fa36663e7ecae0c8e6f0e01b8f Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 14 Aug 2016 13:31:25 +0900 Subject: [PATCH 079/179] debian: changelog stanza for 0.22.1-3 --- debian/changelog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/debian/changelog b/debian/changelog index bac4fcb9..1a7150d0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +notmuch (0.22.1-3) unstable; urgency=medium + + * Gag gdb even more. Bug fix: "FTBFS in testing", thanks to Santiago + Vila (Closes: #834271). + + -- David Bremner Sun, 14 Aug 2016 13:31:13 +0900 + notmuch (0.22.1-2) unstable; urgency=medium * Add explicit build-depends on gnupg, for the test suite. From 9e177b236c7054bc6a51c4718bfd4b31cd4f8b20 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Thu, 18 Aug 2016 05:06:54 -0300 Subject: [PATCH 080/179] lib: reword comment about XFOLDER: prefix I believe the current one is misleading, because in my experiments Xapian did not add : when prefix and term were both upper case. Indeed, it's hard to see how it could, because prefixes are added at a layer above Xapian in our code. See _notmuch_message_add_term for an example. Also try to explain why this is a good idea. As far as I can ascertain, this is more of an issue for a system trying to work with an unknown set of prefixes. Since notmuch has a fixed set of prefixes, and we can hopefully be trusted not to add XGOLD and XGOLDEN as prefixes, it is harder for problems to arise. --- lib/database.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/database.cc b/lib/database.cc index 57a98c94..5577aaf9 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -260,10 +260,10 @@ static prefix_t BOOLEAN_PREFIX_EXTERNAL[] = { { "id", "Q" }, { "path", "P" }, /* - * Without the ":", since this is a multi-letter prefix, Xapian - * will add a colon itself if the first letter of the path is - * upper-case ASCII. Including the ":" forces there to always be a - * colon, which keeps our own logic simpler. + * Unconditionally add ':' to reduce potential ambiguity with + * overlapping prefixes and/or terms that start with capital + * letters. See Xapian document termprefixes.html for related + * discussion. */ { "folder", "XFOLDER:" }, }; From 3ba1b938b56857dc18e4d2b7233b8aba5cad7820 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Wed, 29 Jun 2016 21:39:11 +0200 Subject: [PATCH 081/179] doc: update cross references for notmuch.1 In particular add notmuch-compact(1) (Debian bug #825884) --- doc/man1/notmuch.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/man1/notmuch.rst b/doc/man1/notmuch.rst index edd04efb..7429f517 100644 --- a/doc/man1/notmuch.rst +++ b/doc/man1/notmuch.rst @@ -138,11 +138,11 @@ of notmuch. SEE ALSO ======== -**notmuch-config(1)**, **notmuch-count(1)**, **notmuch-dump(1)**, -**notmuch-hooks(5)**, **notmuch-insert(1)**, **notmuch-new(1)**, -**notmuch-reply(1)**, **notmuch-restore(1)**, **notmuch-search(1)**, -**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**, -**notmuch-address(1)** +**notmuch-address(1)**, **notmuch-compact(1)**, **notmuch-config(1)**, +**notmuch-count(1)**, **notmuch-dump(1)**, **notmuch-hooks(5)**, +**notmuch-insert(1)**, **notmuch-new(1)**, **notmuch-reply(1)**, +**notmuch-restore(1)**, **notmuch-search(1)**, +**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)** The notmuch website: **https://notmuchmail.org** From 59fed50a82b0a625a7303099675484557fed9d23 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Mon, 27 Jun 2016 22:43:04 +0200 Subject: [PATCH 082/179] lib: update cached mtime in notmuch_directory_set_mtime Without this change, the following code fails notmuch_directory_set_mtime(dir, 12345); assert(notmuch_directory_get_mtime(dir) == 12345); --- lib/directory.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/directory.cc b/lib/directory.cc index a19f7779..5de3319c 100644 --- a/lib/directory.cc +++ b/lib/directory.cc @@ -227,6 +227,9 @@ notmuch_directory_set_mtime (notmuch_directory_t *directory, Xapian::sortable_serialise (mtime)); db->replace_document (directory->document_id, directory->doc); + + directory->mtime = mtime; + } catch (const Xapian::Error &error) { _notmuch_database_log (notmuch, "A Xapian exception occurred setting directory mtime: %s.\n", From 2f2df00d436b117acd3e0de13548406efaf272b8 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 23 Aug 2016 20:40:51 -0300 Subject: [PATCH 083/179] contrib: remove old copy of vim front end Having two in the tree is just confusing. And the viml one is even more out of date than the ruby one. --- contrib/notmuch-vim/Makefile | 23 - contrib/notmuch-vim/README | 86 - contrib/notmuch-vim/notmuch.yaml | 8 - contrib/notmuch-vim/plugin/notmuch.vim | 1432 ----------------- .../notmuch-vim/syntax/notmuch-compose.vim | 7 - .../notmuch-vim/syntax/notmuch-folders.vim | 12 - .../notmuch-vim/syntax/notmuch-git-diff.vim | 26 - contrib/notmuch-vim/syntax/notmuch-search.vim | 12 - contrib/notmuch-vim/syntax/notmuch-show.vim | 24 - 9 files changed, 1630 deletions(-) delete mode 100644 contrib/notmuch-vim/Makefile delete mode 100644 contrib/notmuch-vim/README delete mode 100644 contrib/notmuch-vim/notmuch.yaml delete mode 100644 contrib/notmuch-vim/plugin/notmuch.vim delete mode 100644 contrib/notmuch-vim/syntax/notmuch-compose.vim delete mode 100644 contrib/notmuch-vim/syntax/notmuch-folders.vim delete mode 100644 contrib/notmuch-vim/syntax/notmuch-git-diff.vim delete mode 100644 contrib/notmuch-vim/syntax/notmuch-search.vim delete mode 100644 contrib/notmuch-vim/syntax/notmuch-show.vim diff --git a/contrib/notmuch-vim/Makefile b/contrib/notmuch-vim/Makefile deleted file mode 100644 index f17bebfe..00000000 --- a/contrib/notmuch-vim/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -.PHONY: all help install link symlink - -files = plugin/notmuch.vim \ - $(wildcard syntax/notmuch-*.vim) -prefix = $(HOME)/.vim -destdir = $(prefix)/plugin - -INSTALL = install -D -m644 - -all: help - -help: - @echo "I don't actually build anything, but I will help you install" - @echo "notmuch support for vim." - @echo - @echo " make install - copy plugin scripts and syntax files to ~/.vim" - @echo " make symlink - create symlinks in ~/.vim (useful for development)" - -install: - @for x in $(files); do $(INSTALL) $(CURDIR)/$$x $(prefix)/$$x; done - -link symlink: INSTALL = ln -fs -link symlink: install diff --git a/contrib/notmuch-vim/README b/contrib/notmuch-vim/README deleted file mode 100644 index 53f1c4e1..00000000 --- a/contrib/notmuch-vim/README +++ /dev/null @@ -1,86 +0,0 @@ -This directory contains a vim script that allows reading notmuch mail -through vim. - -NOTE: this is a work in progress. Patches welcome. - -Dependencies: - notmuch: - Naturally, it expects you have notmuch installed and configured. - - sendmail: - To send mail, notmuch.vim uses sendmail as default. Most modern MTAs - provide a compatibility binary, and so should work well. - - -To install: - make install - - -To run: - vim -c ':NotMuch' - - from vim: - :NotMuch - :NotMuch new to:bart@jukie.net 'subject:this is a test' - - -Buffer types: - [notmuch-folders] - Folder list, or technically a list of saved searches. - - Keybindings: - - show the selected search - m - compose a new message - s - enter search criteria - = - refresh display - - [notmuch-search] - You are presented with the search results when you run :NotMuch. - - Keybindings: - - show the selected thread collapsing unmatched items - - show the entire selected thread - a - archive message (remove inbox tag) - f - filter the current search terms - o - toggle search screen order - m - compose a new message - r - reply to thread - s - enter search criteria - ,s - alter search criteria - t - filter the current search terms with tags - q - return to folder display, or undo filter - + - add tag(s) to selected message - - - remove tag(s) from selected message - = - refresh display - ? - reveal the thread ID of what's under cursor - ^] - search using word under cursor - - [notmuch-show] - This is the display of the message. - - Keybindings: - - mark read, archive, go to next matching message - ^n - next message - ^p - previous message - b - toggle folding of message bodies - c - toggle folding of citations - h - toggle folding of extra header lines - i - toggle folding of signatures - m - compose a new message - r - reply to the message - s - enter search criteria - q - return to search display - ? - reveal the message and thread IDs of what's under cursor - ^] - search using word under cursor - - [notmuch-compose] - When you're writing an email, you're in this mode. - - Insert-mode keybindings: - - go to the next header line - - Normal-mode keybindings: - - go to the next header line - ,s - send this message - ,q - abort this message - diff --git a/contrib/notmuch-vim/notmuch.yaml b/contrib/notmuch-vim/notmuch.yaml deleted file mode 100644 index 3d8422c8..00000000 --- a/contrib/notmuch-vim/notmuch.yaml +++ /dev/null @@ -1,8 +0,0 @@ -addon: notmuch -description: "notmuch mail user interface" -files: - - plugin/notmuch.vim - - syntax/notmuch-compose.vim - - syntax/notmuch-folders.vim - - syntax/notmuch-search.vim - - syntax/notmuch-show.vim diff --git a/contrib/notmuch-vim/plugin/notmuch.vim b/contrib/notmuch-vim/plugin/notmuch.vim deleted file mode 100644 index eaa984bc..00000000 --- a/contrib/notmuch-vim/plugin/notmuch.vim +++ /dev/null @@ -1,1432 +0,0 @@ -" notmuch.vim plugin --- run notmuch within vim -" -" Copyright © Carl Worth -" -" This file is part of Notmuch. -" -" Notmuch is free software: you can redistribute it and/or modify it -" under the terms of the GNU General Public License as published by -" the Free Software Foundation, either version 3 of the License, or -" (at your option) any later version. -" -" Notmuch is distributed in the hope that it will be useful, but -" WITHOUT ANY WARRANTY; without even the implied warranty of -" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -" General Public License for more details. -" -" You should have received a copy of the GNU General Public License -" along with Notmuch. If not, see . -" -" Authors: Bart Trojanowski -" Contributors: Felipe Contreras , -" Peter Hartman -" -" --- configuration defaults {{{1 - -let s:notmuch_defaults = { - \ 'g:notmuch_cmd': 'notmuch' , - \ 'g:notmuch_sendmail': '/usr/sbin/sendmail' , - \ 'g:notmuch_debug': 0 , - \ - \ 'g:notmuch_search_newest_first': 1 , - \ 'g:notmuch_search_from_column_width': 20 , - \ - \ 'g:notmuch_show_fold_signatures': 1 , - \ 'g:notmuch_show_fold_citations': 1 , - \ 'g:notmuch_show_fold_bodies': 0 , - \ 'g:notmuch_show_fold_headers': 1 , - \ - \ 'g:notmuch_show_message_begin_regexp': ' message{' , - \ 'g:notmuch_show_message_end_regexp': ' message}' , - \ 'g:notmuch_show_header_begin_regexp': ' header{' , - \ 'g:notmuch_show_header_end_regexp': ' header}' , - \ 'g:notmuch_show_body_begin_regexp': ' body{' , - \ 'g:notmuch_show_body_end_regexp': ' body}' , - \ 'g:notmuch_show_attachment_begin_regexp': ' attachment{' , - \ 'g:notmuch_show_attachment_end_regexp': ' attachment}' , - \ 'g:notmuch_show_part_begin_regexp': ' part{' , - \ 'g:notmuch_show_part_end_regexp': ' part}' , - \ 'g:notmuch_show_marker_regexp': ' \\(message\\|header\\|body\\|attachment\\|part\\)[{}].*$', - \ - \ 'g:notmuch_show_message_parse_regexp': '\(id:[^ ]*\) depth:\([0-9]*\) match:\([0-9]*\) excluded:\([0-9]*\) filename:\(.*\)$', - \ 'g:notmuch_show_tags_regexp': '(\([^)]*\))$' , - \ - \ 'g:notmuch_show_signature_regexp': '^\(-- \?\|_\+\)$' , - \ 'g:notmuch_show_signature_lines_max': 12 , - \ - \ 'g:notmuch_show_citation_regexp': '^\s*>' , - \ - \ 'g:notmuch_compose_insert_mode_start': 1 , - \ 'g:notmuch_compose_header_help': 1 , - \ 'g:notmuch_compose_temp_file_dir': '~/.notmuch/compose' , - \ } - -" defaults for g:notmuch_initial_search_words -" override with: let g:notmuch_initial_search_words = [ ... ] -let s:notmuch_initial_search_words_defaults = [ - \ 'tag:inbox and tag:unread', - \ ] - -" defaults for g:notmuch_show_headers -" override with: let g:notmuch_show_headers = [ ... ] -let s:notmuch_show_headers_defaults = [ - \ 'Subject', - \ 'To', - \ 'Cc', - \ 'Bcc', - \ 'Date', - \ ] - -" defaults for g:notmuch_folders -" override with: let g:notmuch_folders = [ ... ] -let s:notmuch_folders_defaults = [ - \ [ 'new', 'tag:inbox and tag:unread' ], - \ [ 'inbox', 'tag:inbox' ], - \ [ 'unread', 'tag:unread' ], - \ ] - -" defaults for g:notmuch_signature -" override with: let g:notmuch_signature = [ ... ] -let s:notmuch_signature_defaults = [ - \ '', - \ '-- ', - \ 'email sent from notmuch.vim plugin' - \ ] - -" defaults for g:notmuch_compose_headers -" override with: let g:notmuch_compose_headers = [ ... ] -let s:notmuch_compose_headers_defaults = [ - \ 'From', - \ 'To', - \ 'Cc', - \ 'Bcc', - \ 'Subject' - \ ] - -" --- keyboard mapping definitions {{{1 - -" --- --- bindings for folders mode {{{2 - -let g:notmuch_folders_maps = { - \ 'm': ':call NM_new_mail()', - \ 's': ':call NM_search_prompt()', - \ 'q': ':call NM_kill_this_buffer()', - \ '=': ':call NM_folders_refresh_view()', - \ '': ':call NM_folders_show_search()', - \ } - -" --- --- bindings for search screen {{{2 -let g:notmuch_search_maps = { - \ '': ':call NM_search_show_thread(0)', - \ '': ':call NM_search_show_thread(1)', - \ '': ':call NM_search_expand('''')', - \ 'I': ':call NM_search_mark_read_thread()', - \ 'a': ':call NM_search_archive_thread()', - \ 'A': ':call NM_search_mark_read_then_archive_thread()', - \ 'D': ':call NM_search_delete_thread()', - \ 'f': ':call NM_search_filter()', - \ 'm': ':call NM_new_mail()', - \ 'o': ':call NM_search_toggle_order()', - \ 'r': ':call NM_search_reply_to_thread()', - \ 's': ':call NM_search_prompt()', - \ ',s': ':call NM_search_edit()', - \ 't': ':call NM_search_filter_by_tag()', - \ 'q': ':call NM_kill_this_buffer()', - \ '+': ':call NM_search_add_tags([])', - \ '-': ':call NM_search_remove_tags([])', - \ '=': ':call NM_search_refresh_view()', - \ '?': ':echo NM_search_thread_id() . '' @ '' . join(NM_get_search_words())', - \ } - -" --- --- bindings for show screen {{{2 -let g:notmuch_show_maps = { - \ '': ':call NM_show_previous(1, 0)', - \ '': ':call NM_show_next(1, 0)', - \ '': ':call NM_search_expand('''')', - \ 'q': ':call NM_kill_this_buffer()', - \ 's': ':call NM_search_prompt()', - \ - \ 'b': ':call NM_show_fold_toggle(''b'', ''bdy'', !g:notmuch_show_fold_bodies)', - \ 'c': ':call NM_show_fold_toggle(''c'', ''cit'', !g:notmuch_show_fold_citations)', - \ 'h': ':call NM_show_fold_toggle(''h'', ''hdr'', !g:notmuch_show_fold_headers)', - \ 'i': ':call NM_show_fold_toggle(''i'', ''sig'', !g:notmuch_show_fold_signatures)', - \ - \ 'I': ':call NM_show_mark_read_thread()', - \ 'a': ':call NM_show_archive_thread()', - \ 'A': ':call NM_show_mark_read_then_archive_thread()', - \ 'D': ':call NM_show_delete_thread()', - \ 'd': ':call NM_show_delete_message()', - \ 'N': ':call NM_show_mark_read_then_next_open_message()', - \ 'v': ':call NM_show_view_all_mime_parts()', - \ '+': ':call NM_show_add_tag()', - \ '-': ':call NM_show_remove_tag()', - \ '': ':call NM_show_advance_marking_read_and_archiving()', - \ '\|': ':call NM_show_pipe_message()', - \ - \ '': ':call NM_show_previous_fold()', - \ '': ':call NM_show_next_fold()', - \ '': ':call NM_show_toggle_fold()', - \ - \ 'r': ':call NM_show_reply()', - \ 'm': ':call NM_new_mail()', - \ '?': ':echo NM_show_message_id() . '' @ '' . join(NM_get_search_words())', - \ } - -" --- --- bindings for compose screen {{{2 -let g:notmuch_compose_nmaps = { - \ ',s': ':call NM_compose_send()', - \ ',a': ':call NM_compose_attach()', - \ ',q': ':call NM_kill_this_buffer()', - \ '': ':call NM_compose_next_entry_area()', - \ } -let g:notmuch_compose_imaps = { - \ '': '=NM_compose_next_entry_area()', - \ } - -" --- implement folders screen {{{1 - -function! s:NM_cmd_folders(words) - if len(a:words) - throw 'Not expecting any arguments for folders command.' - endif - let cmd = ['count'] - let disp = [] - let searches = [] - for entry in g:notmuch_folders - let [ name, search ] = entry - let data = s:NM_run(cmd + [search]) - let cnt = matchlist(data, '\(\d\+\)')[1] - call add(disp, printf('%9d %-20s (%s)', cnt, name, search)) - call add(searches, search) - endfor - - call NM_newBuffer('', 'folders', join(disp, "\n")) - let b:nm_searches = searches - let b:nm_timestamp = reltime() - - call NM_cmd_folders_mksyntax() - call NM_set_map('n', g:notmuch_folders_maps) - setlocal cursorline - setlocal nowrap -endfunction - -function! s:NM_cmd_folders_mksyntax() -endfunction - -" --- --- folders screen action functions {{{2 - -function! s:NM_folders_refresh_view() - let lno = line('.') - setlocal bufhidden=delete - call s:NM_cmd_folders([]) - exec printf('norm %dG', lno) -endfunction - -function! s:NM_folders_show_search() - let line = line('.') - let search = b:nm_searches[line-1] - - let prev_bufnr = bufnr('%') - setlocal bufhidden=hide - call NM_cmd_search([search]) - setlocal bufhidden=delete - let b:nm_prev_bufnr = prev_bufnr -endfunction - - -" --- implement search screen {{{1 - -function! s:NM_cmd_search(words) - let cmd = ['search'] - if g:notmuch_search_newest_first - let cmd = cmd + ['--sort=newest-first'] - else - let cmd = cmd + ['--sort=oldest-first'] - endif - let data = s:NM_run(cmd + a:words) - let lines = split(data, "\n") - let disp = copy(lines) - call map(disp, 's:NM_cmd_search_fmtline(v:val)') - - call NM_newBuffer('', 'search', join(disp, "\n")) - let b:nm_raw_lines = lines - let b:nm_search_words = a:words - - call NM_set_map('n', g:notmuch_search_maps) - setlocal cursorline - setlocal nowrap -endfunction -function! s:NM_cmd_search_fmtline(line) - let m = matchlist(a:line, '^\(thread:\S\+\)\s\(.\{12\}\) \[\(\d\+\)/\d\+\] \([^;]\+\); \%(\[[^\[]\+\] \)*\(.*\) (\([^(]*\))$') - if !len(m) - return 'ERROR PARSING: ' . a:line - endif - let max = g:notmuch_search_from_column_width - let flist = {} - for at in split(m[4], '[|,] ') - let p = split(at, '[@.]') - let flist[p[0]] = 1 - endfor - let from = join(keys(flist), ", ") - return printf("%-12s %3s %-20.20s | %s (%s)", m[2], m[3], from, m[5], m[6]) -endfunction - -" --- --- search screen action functions {{{2 - -function! s:NM_search_show_thread(everything) - let words = [ NM_search_thread_id() ] - if !a:everything && exists('b:nm_search_words') - call extend(words, ['AND', '(']) - call extend(words, b:nm_search_words) - call add(words, ')') - endif - call NM_cmd_show(words) - let b:nm_show_everything = a:everything -endfunction - -function! s:NM_search_prompt() - " TODO: input() can support completion - let text = input('NotMuch Search: ') - if strlen(text) - let tags = split(text) - else - let tags = s:notmuch_initial_search_words_defaults - endif - let prev_bufnr = bufnr('%') - if b:nm_type == 'search' && exists('b:nm_prev_bufnr') - " TODO: we intend to replace the current buffer, - " ... maybe we could just clear it - let prev_bufnr = b:nm_prev_bufnr - setlocal bufhidden=delete - else - setlocal bufhidden=hide - endif - call NM_cmd_search(tags) - setlocal bufhidden=delete - let b:nm_prev_bufnr = prev_bufnr -endfunction - -function! s:NM_search_edit() - " TODO: input() can support completion - let text = input('NotMuch Search: ', join(b:nm_search_words, ' ')) - if strlen(text) - call NM_cmd_search(split(text)) - endif -endfunction - -function! s:NM_search_mark_read_thread() - call NM_tag([], ['-unread']) - norm j -endfunction - -function! s:NM_search_archive_thread() - call NM_tag([], ['-inbox']) - norm j -endfunction - -function! s:NM_search_mark_read_then_archive_thread() - call NM_tag([], ['-unread', '-inbox']) - norm j -endfunction - -function! s:NM_search_delete_thread() - call NM_tag([], ['+delete','-inbox','-unread']) - norm j -endfunction - -function! s:NM_search_filter() - call NM_search_filter_helper('Filter: ', '', '') -endfunction - -function! s:NM_search_filter_by_tag() - call NM_search_filter_helper('Filter Tag(s): ', 'tag:', 'and') -endfunction - -function! s:NM_search_filter_helper(prompt, prefix, joiner) - " TODO: input() can support completion - let text = substitute(input(a:prompt), '\v(^\s*|\s*$|\n)', '', 'g') - if !strlen(text) - return - endif - - let tags = b:nm_search_words + ['AND'] - \ + NM_combine_tags(a:prefix, split(text), a:joiner, '()') - - let prev_bufnr = bufnr('%') - setlocal bufhidden=hide - call NM_cmd_search(tags) - setlocal bufhidden=delete - let b:nm_prev_bufnr = prev_bufnr -endfunction - -function! s:NM_search_toggle_order() - let g:notmuch_search_newest_first = !g:notmuch_search_newest_first - " FIXME: maybe this would be better done w/o reading re-reading the lines - " reversing the b:nm_raw_lines and the buffer lines would be better - call NM_search_refresh_view() -endfunction - -function! s:NM_search_reply_to_thread() - let cmd = ['reply'] - call add(cmd, NM_search_thread_id()) - call add(cmd, 'AND') - call extend(cmd, NM_get_search_words()) - - let data = NM_run(cmd) - let lines = split(data, "\n") - call NM_newComposeBuffer(lines, 0) -endfunction - -function! s:NM_search_add_tags(tags) - call NM_search_add_remove_tags('Add Tag(s): ', '+', a:tags) -endfunction - -function! s:NM_search_remove_tags(tags) - call NM_search_add_remove_tags('Remove Tag(s): ', '-', a:tags) -endfunction - -function! s:NM_search_refresh_view() - let lno = line('.') - let prev_bufnr = b:nm_prev_bufnr - setlocal bufhidden=delete - call NM_cmd_search(b:nm_search_words) - let b:nm_prev_bufnr = prev_bufnr - " FIXME: should find the line of the thread we were on if possible - exec printf('norm %dG', lno) -endfunction - -" --- --- search screen helper functions {{{2 - -function! s:NM_search_thread_id() - if !exists('b:nm_raw_lines') - throw 'Eeek! no b:nm_raw_lines' - endif - let mnum = line('.') - 1 - if len(b:nm_raw_lines) <= mnum - return '' - endif - let info = b:nm_raw_lines[mnum] - let what = split(info, '\s\+')[0] - return what -endfunction - -function! s:NM_search_add_remove_tags(prompt, prefix, intags) - if type(a:intags) != type([]) || len(a:intags) == 0 - " TODO: input() can support completion - let text = input(a:prompt) - if !strlen(text) - return - endif - let tags = split(text, ' ') - else - let tags = a:intags - endif - call map(tags, 'a:prefix . v:val') - call NM_tag([], tags) -endfunction - -" --- implement show screen {{{1 - -function! s:NM_cmd_show(words) - let prev_bufnr = bufnr('%') - let data = s:NM_run(['show', '--entire-thread'] + a:words) - let lines = split(data, "\n") - - let info = s:NM_cmd_show_parse(lines) - - setlocal bufhidden=hide - call NM_newBuffer('', 'show', join(info['disp'], "\n")) - setlocal bufhidden=delete - let b:nm_search_words = a:words - let b:nm_raw_info = info - let b:nm_prev_bufnr = prev_bufnr - - call NM_cmd_show_mkfolds() - call NM_cmd_show_mksyntax() - call NM_set_map('n', g:notmuch_show_maps) - setlocal foldtext=NM_cmd_show_foldtext() - setlocal fillchars= - setlocal foldcolumn=6 - -endfunction - -function! s:NM_show_previous(can_change_thread, find_matching) - let everything = exists('b:nm_show_everything') ? b:nm_show_everything : 0 - let info = b:nm_raw_info - let lnum = line('.') - for msg in reverse(copy(info['msgs'])) - if a:find_matching && msg['match'] == '0' - continue - endif - if lnum <= msg['start'] - continue - endif - - exec printf('norm %dGzt', msg['start']) - " TODO: try to fit the message on screen - return - endfor - if !a:can_change_thread - return - endif - call NM_kill_this_buffer() - if line('.') > 1 - norm k - call NM_search_show_thread(everything) - norm G - call NM_show_previous(0, a:find_matching) - else - echo 'No more messages.' - endif -endfunction - -function! s:NM_show_next(can_change_thread, find_matching) - let info = b:nm_raw_info - let lnum = line('.') - for msg in info['msgs'] - if a:find_matching && msg['match'] == '0' - continue - endif - if lnum >= msg['start'] - continue - endif - - exec printf('norm %dGzt', msg['start']) - " TODO: try to fit the message on screen - return - endfor - if a:can_change_thread - call NM_show_next_thread() - endif -endfunction - -function! s:NM_show_next_thread() - let everything = exists('b:nm_show_everything') ? b:nm_show_everything : 0 - call NM_kill_this_buffer() - if line('.') != line('$') - norm j - call NM_search_show_thread(everything) - else - echo 'No more messages.' - endif -endfunction - -function! s:NM_show_mark_read_thread() - call NM_tag(b:nm_search_words, ['-unread']) - call NM_show_next_thread() -endfunction - -function! s:NM_show_archive_thread() - call NM_tag(b:nm_search_words, ['-inbox']) - call NM_show_next_thread() -endfunction - -function! s:NM_show_mark_read_then_archive_thread() - call NM_tag(b:nm_search_words, ['-unread', '-inbox']) - call NM_show_next_thread() -endfunction - -function! s:NM_show_delete_thread() - call NM_tag(b:nm_search_words, ['+delete', '-inbox', '-unread']) - call NM_show_next_thread() -endfunction - -function! s:NM_show_delete_message() - let msg = NM_show_get_message_for_line(line('.')) - call NM_tag([msg['id']], ['+delete', '-inbox', '-unread']) -endfunction - -function! s:NM_show_mark_read_then_next_open_message() - echo 'not implemented' -endfunction - -function! s:NM_show_previous_message() - echo 'not implemented' -endfunction - -function! s:NM_show_reply() - let cmd = ['reply'] - call add(cmd, NM_show_message_id()) - call add(cmd, 'AND') - call extend(cmd, NM_get_search_words()) - - let data = NM_run(cmd) - let lines = split(data, "\n") - call NM_newComposeBuffer(lines, 0) -endfunction - -function! s:NM_show_view_all_mime_parts() - echo 'not implemented' -endfunction - -function! s:NM_show_view_raw_message() - echo 'not implemented' -endfunction - -function! s:NM_show_add_tag() - echo 'not implemented' -endfunction - -function! s:NM_show_remove_tag() - echo 'not implemented' -endfunction - -" if entire message is not visible scroll down 1/2 page or less to get to the bottom of message -" otherwise go to next message -" any message that is viewed entirely has inbox and unread tags removed -function! s:NM_show_advance_marking_read_and_archiving() - let advance_tags = ['unread', 'inbox'] - - let vis_top = line('w0') - let vis_bot = line('w$') - - let msg_top = NM_show_get_message_for_line(vis_top) - if !has_key(msg_top,'id') - throw "No top visible message." - endif - - " if the top message is the last message, just expunge the entire thread and move on - if msg_top['end'] == line('$') - let ids = [] - for msg in b:nm_raw_info['msgs'] - if has_key(msg,'match') && msg['match'] != '0' - call add(ids, msg['id']) - endif - endfor - let filter = NM_combine_tags('tag:', advance_tags, 'OR', '()') - \ + ['AND'] - \ + NM_combine_tags('', ids, 'OR', '()') - call map(advance_tags, '"-" . v:val') - call NM_tag(filter, advance_tags) - call NM_show_next(1, 1) - return - endif - - let msg_bot = NM_show_get_message_for_line(vis_bot) - if !has_key(msg_bot,'id') - throw "No bottom visible message." - endif - - " if entire message fits on the screen, read/archive it, move to the next one - if msg_top['id'] != msg_bot['id'] || msg_top['end'] <= vis_bot - exec printf('norm %dG', vis_top) - call NM_show_next(0, 1) - if has_key(msg_top,'match') && msg_top['match'] != '0' - redraw - " do this last to hide the latency - let filter = NM_combine_tags('tag:', advance_tags, 'OR', '()') - \ + ['AND', msg_top['id']] - call map(advance_tags, '"-" . v:val') - call NM_tag(filter, advance_tags) - endif - return - endif - - " entire message does not fit on the screen, scroll down to bottom, max 1/2 screen - let jmp = winheight(winnr()) / 2 - let max = msg_bot['end'] - vis_bot - if jmp > max - let jmp = max - endif - exec printf('norm %dGzt', vis_top + jmp) - return -endfunction - -function! s:NM_show_pipe_message() - echo 'not implemented' -endfunction - -function! s:NM_show_previous_fold() - echo 'not implemented' -endfunction - -function! s:NM_show_next_fold() - echo 'not implemented' -endfunction - -function! s:NM_show_toggle_fold() - echo 'not implemented' -endfunction - - -" --- --- show screen helper functions {{{2 - -function! s:NM_show_get_message_for_line(line) - for msg in b:nm_raw_info['msgs'] - if a:line > msg['end'] - continue - endif - return msg - endfor - return {} -endfunction - -function! s:NM_show_message_id() - if !exists('b:nm_raw_info') - throw 'Eeek! no b:nm_raw_info' - endif - let msg = NM_show_get_message_for_line(line('.')) - if has_key(msg,'id') - return msg['id'] - endif - return '' -endfunction - -function! s:NM_show_fold_toggle(key, type, fold) - let info = b:nm_raw_info - let act = 'open' - if a:fold - let act = 'close' - endif - for fld in info['folds'] - if fld[0] != a:type - continue - endif - "let idx = fld[3] - "let msg = info['msgs'][idx] - "if has_key(msg,'match') && msg['match'] == '0' - " continue - "endif - let cls = foldclosed(fld[1]) - if cls != -1 && cls != fld[1] - continue - endif - exec printf('%dfold%s', fld[1], act) - endfor - exec printf('nnoremap %s :call NM_show_fold_toggle(''%s'', ''%s'', %d)', a:key, a:key, a:type, !a:fold) -endfunction - - -" s:NM_cmd_show_parse returns the following dictionary: -" 'disp': lines to display -" 'msgs': message info dicts { start, end, id, depth, filename, descr, header } -" 'folds': fold info arrays [ type, start, end ] -" 'foldtext': fold text indexed by start line -function! s:NM_cmd_show_parse(inlines) - let info = { 'disp': [], - \ 'msgs': [], - \ 'folds': [], - \ 'foldtext': {} } - let msg = {} - let hdr = {} - - let in_message = 0 - let in_header = 0 - let in_body = 0 - let in_part = '' - - let body_start = -1 - let part_start = -1 - - let mode_type = '' - let mode_start = -1 - - let inlnum = 0 - for line in a:inlines - let inlnum = inlnum + 1 - let foldinfo = [] - - if strlen(in_part) - let part_end = 0 - - if match(line, g:notmuch_show_part_end_regexp) != -1 - let part_end = len(info['disp']) - else - call add(info['disp'], line) - endif - - if in_part == 'text/plain' - if !part_end && mode_type == '' - if match(line, g:notmuch_show_signature_regexp) != -1 - let mode_type = 'sig' - let mode_start = len(info['disp']) - elseif match(line, g:notmuch_show_citation_regexp) != -1 - let mode_type = 'cit' - let mode_start = len(info['disp']) - endif - elseif mode_type == 'cit' - if part_end || match(line, g:notmuch_show_citation_regexp) == -1 - let outlnum = len(info['disp']) - if !part_end - let outlnum = outlnum - 1 - endif - let foldinfo = [ mode_type, mode_start, outlnum, len(info['msgs']), - \ printf('[ %d-line citation. Press "c" to show. ]', 1 + outlnum - mode_start) ] - let mode_type = '' - endif - elseif mode_type == 'sig' - let outlnum = len(info['disp']) - if (outlnum - mode_start) > g:notmuch_show_signature_lines_max - let mode_type = '' - elseif part_end - let foldinfo = [ mode_type, mode_start, outlnum, len(info['msgs']), - \ printf('[ %d-line signature. Press "i" to show. ]', 1 + outlnum - mode_start) ] - let mode_type = '' - endif - endif - endif - - if part_end - " FIXME: this is a hack for handling two folds being added for one line - " we should handle adding a fold in a function - if len(foldinfo) && foldinfo[1] < foldinfo[2] - call add(info['folds'], foldinfo[0:3]) - let info['foldtext'][foldinfo[1]] = foldinfo[4] - endif - - let foldinfo = [ 'text', part_start, part_end, len(info['msgs']), - \ printf('[ %d-line %s. Press "p" to show. ]', part_end - part_start, in_part) ] - let in_part = '' - call add(info['disp'], '') - endif - - elseif in_body - if !has_key(msg,'body_start') - let msg['body_start'] = len(info['disp']) + 1 - endif - if match(line, g:notmuch_show_body_end_regexp) != -1 - let body_end = len(info['disp']) - let foldinfo = [ 'bdy', body_start, body_end, len(info['msgs']), - \ printf('[ BODY %d - %d lines ]', len(info['msgs']), body_end - body_start) ] - - let in_body = 0 - - elseif match(line, g:notmuch_show_part_begin_regexp) != -1 - let m = matchlist(line, 'ID: \(\d\+\), Content-type: \(\S\+\)') - let in_part = 'unknown' - if len(m) - let in_part = m[2] - endif - call add(info['disp'], - \ printf('--- %s ---', in_part)) - " We don't yet handle nested parts, so pop - " multipart/* immediately so text/plain - " sub-parts are parsed properly - if match(in_part, '^multipart/') != -1 - let in_part = '' - else - let part_start = len(info['disp']) + 1 - endif - endif - - elseif in_header - if in_header == 1 - let msg['descr'] = line - call add(info['disp'], line) - let in_header = 2 - let msg['hdr_start'] = len(info['disp']) + 1 - - else - if match(line, g:notmuch_show_header_end_regexp) != -1 - let hdr_start = msg['hdr_start']+1 - let hdr_end = len(info['disp']) - let foldinfo = [ 'hdr', hdr_start, hdr_end, len(info['msgs']), - \ printf('[ %d-line headers. Press "h" to show. ]', hdr_end + 1 - hdr_start) ] - let msg['header'] = hdr - let in_header = 0 - let hdr = {} - else - let m = matchlist(line, '^\(\w\+\):\s*\(.*\)$') - if len(m) - let hdr[m[1]] = m[2] - if match(g:notmuch_show_headers, m[1]) != -1 - call add(info['disp'], line) - endif - endif - endif - endif - - elseif in_message - if match(line, g:notmuch_show_message_end_regexp) != -1 - let msg['end'] = len(info['disp']) - call add(info['disp'], '') - - let foldinfo = [ 'msg', msg['start'], msg['end'], len(info['msgs']), - \ printf('[ MSG %d - %s ]', len(info['msgs']), msg['descr']) ] - - call add(info['msgs'], msg) - let msg = {} - let in_message = 0 - let in_header = 0 - let in_body = 0 - let in_part = '' - - elseif match(line, g:notmuch_show_header_begin_regexp) != -1 - let in_header = 1 - continue - - elseif match(line, g:notmuch_show_body_begin_regexp) != -1 - let body_start = len(info['disp']) + 1 - let in_body = 1 - continue - endif - - else - if match(line, g:notmuch_show_message_begin_regexp) != -1 - let msg['start'] = len(info['disp']) + 1 - - let m = matchlist(line, g:notmuch_show_message_parse_regexp) - if len(m) - let msg['id'] = m[1] - let msg['depth'] = m[2] - let msg['match'] = m[3] - let msg['excluded'] = m[4] - let msg['filename'] = m[5] - endif - - let in_message = 1 - endif - endif - - if len(foldinfo) && foldinfo[1] < foldinfo[2] - call add(info['folds'], foldinfo[0:3]) - let info['foldtext'][foldinfo[1]] = foldinfo[4] - endif - endfor - return info -endfunction - -function! s:NM_cmd_show_mkfolds() - let info = b:nm_raw_info - - for afold in info['folds'] - exec printf('%d,%dfold', afold[1], afold[2]) - let state = 'open' - if (afold[0] == 'sig' && g:notmuch_show_fold_signatures) - \ || (afold[0] == 'cit' && g:notmuch_show_fold_citations) - \ || (afold[0] == 'bdy' && g:notmuch_show_fold_bodies) - \ || (afold[0] == 'hdr' && g:notmuch_show_fold_headers) - let state = 'close' - elseif afold[0] == 'msg' - let idx = afold[3] - let msg = info['msgs'][idx] - if has_key(msg,'match') && msg['match'] == '0' - let state = 'close' - endif - endif - exec printf('%dfold%s', afold[1], state) - endfor -endfunction - -function! s:NM_cmd_show_mksyntax() - let info = b:nm_raw_info - let cnt = 0 - for msg in info['msgs'] - let cnt = cnt + 1 - let start = msg['start'] - let hdr_start = msg['hdr_start'] - let body_start = msg['body_start'] - let end = msg['end'] - exec printf('syntax region nmShowMsg%dDesc start=''\%%%dl'' end=''\%%%dl'' contains=@nmShowMsgDesc', cnt, start, start+1) - exec printf('syntax region nmShowMsg%dHead start=''\%%%dl'' end=''\%%%dl'' contains=@nmShowMsgHead', cnt, hdr_start, body_start) - exec printf('syntax region nmShowMsg%dBody start=''\%%%dl'' end=''\%%%dl'' contains=@nmShowMsgBody', cnt, body_start, end) - endfor -endfunction - -function! NM_cmd_show_foldtext() - let foldtext = b:nm_raw_info['foldtext'] - return foldtext[v:foldstart] -endfunction - - -" --- implement compose screen {{{1 - -function! s:NM_cmd_compose(words, body_lines) - let lines = [] - let start_on_line = 0 - - let hdrs = { } - for word in a:words - let m = matchlist(word, '^\(\w[^:]*\):\s*\(.*\)\s*$') - if !len(m) - throw 'Eeek! bad parameter ''' . string(word) . '''' - endif - let key = substitute(m[1], '\<\w', '\U&', 'g') - if !has_key(hdrs, key) - let hdrs[key] = [] - endif - if strlen(m[2]) - call add(hdrs[key], m[2]) - endif - endfor - - if !has_key(hdrs, 'From') || !len(hdrs['From']) - let me = NM_compose_get_user_email() - let hdrs['From'] = [ me ] - endif - - for key in g:notmuch_compose_headers - let text = has_key(hdrs, key) ? join(hdrs[key], ', ') : '' - call add(lines, key . ': ' . text) - if !start_on_line && !strlen(text) - let start_on_line = len(lines) - endif - endfor - - for [key,val] in items(hdrs) - if match(g:notmuch_compose_headers, key) == -1 - let line = key . ': ' . join(val, ', ') - call add(lines, line) - endif - endfor - - call add(lines, '') - if !start_on_line - let start_on_line = len(lines) + 1 - endif - - if len(a:body_lines) - call extend(lines, a:body_lines) - else - call extend(lines, [ '', '' ]) - endif - - call NM_newComposeBuffer(lines, start_on_line) -endfunction - -function! s:NM_compose_send() - call NM_assert_buffer_type('compose') - let fname = expand('%') - let lnum = 1 - let line = getline(lnum) - let lst_hdr = '' - while match(line, '^$') == -1 - if !exists("hdr_starts") && match(line, '^Notmuch-Help:') == -1 - let hdr_starts = lnum - 1 - endif - let lnum = lnum + 1 - let line = getline(lnum) - endwhile - let body_starts = lnum - 1 - - call append(body_starts, 'Date: ' . strftime('%a, %d %b %Y %H:%M:%S %z')) - exec printf(':0,%dd', hdr_starts) - write - - let line = getline(1) - let m = matchlist(line, '^From:\s*\(.*\)\s*<\(.*\)>$') - if (len(m) >= 2) - let from = m[2] - else - let m = matchlist(line, '^From:\s*\(.*\)$') - let from = m[1] - endif - - let cmdtxt = g:notmuch_sendmail . ' -t -f ' . from . ' < ' . fname - let out = system(cmdtxt) - let err = v:shell_error - if err - undo - write - call NM_newBuffer('new', 'error', - \ "While running...\n" . - \ ' ' . cmdtxt . "\n" . - \ "\n" . - \ "Failed with...\n" . - \ substitute(out, '^', ' ', 'g')) - echohl Error - echo 'Eeek! unable to send mail' - echohl None - return - endif - - if !exists('b:nm_prev_bufnr') - bdelete - else - let prev_bufnr = b:nm_prev_bufnr - bdelete - if prev_bufnr == bufnr('%') - exec printf("buffer %d", prev_bufnr) - endif - endif - call delete(fname) - echo 'Mail sent successfully.' -endfunction - -function! s:NM_compose_attach() - echo 'not implemented' -endfunction - -function! s:NM_compose_next_entry_area() - let lnum = line('.') - let hdr_end = NM_compose_find_line_match(1,'^$',1) - if lnum < hdr_end - let lnum = lnum + 1 - let line = getline(lnum) - if match(line, '^\([^:]\+\):\s*$') == -1 - call cursor(lnum, strlen(line) + 1) - return '' - endif - while match(getline(lnum+1), '^\s') != -1 - let lnum = lnum + 1 - endwhile - call cursor(lnum, strlen(getline(lnum)) + 1) - return '' - - elseif lnum == hdr_end - call cursor(lnum+1, strlen(getline(lnum+1)) + 1) - return '' - endif - if mode() == 'i' - if !getbufvar(bufnr('.'), '&et') - return "\t" - endif - let space = '' - let shiftwidth = a:shiftwidth - let shiftwidth = shiftwidth - ((virtcol('.')-1) % shiftwidth) - " we assume no one has shiftwidth set to more than 40 :) - return ' '[0:shiftwidth] - endif -endfunction - -" --- --- compose screen helper functions {{{2 - -function! s:NM_compose_get_user_email() - " TODO: do this properly (still), i.e., allow for multiple email accounts - let email = substitute(system('notmuch config get user.primary_email'), '\v(^\s*|\s*$|\n)', '', 'g') - return email -endfunction - -function! s:NM_compose_find_line_match(start, pattern, failure) - let lnum = a:start - let lend = line('$') - while lnum < lend - if match(getline(lnum), a:pattern) != -1 - return lnum - endif - let lnum = lnum + 1 - endwhile - return a:failure -endfunction - - -" --- notmuch helper functions {{{1 - -function! s:NM_newBuffer(how, type, content) - if strlen(a:how) - exec a:how - else - enew - endif - setlocal buftype=nofile readonly modifiable scrolloff=0 sidescrolloff=0 - silent put=a:content - keepjumps 0d - setlocal nomodifiable - execute printf('set filetype=notmuch-%s', a:type) - execute printf('set syntax=notmuch-%s', a:type) - let b:nm_type = a:type -endfunction - -function! s:NM_newFileBuffer(fdir, fname, type, lines) - let fdir = expand(a:fdir) - if !isdirectory(fdir) - call mkdir(fdir, 'p') - endif - let file_name = NM_mktemp(fdir, a:fname) - if writefile(a:lines, file_name) - throw 'Eeek! couldn''t write to temporary file ' . file_name - endif - exec printf('edit %s', file_name) - setlocal buftype= noreadonly modifiable scrolloff=0 sidescrolloff=0 - execute printf('set filetype=notmuch-%s', a:type) - execute printf('set syntax=notmuch-%s', a:type) - let b:nm_type = a:type -endfunction - -function! s:NM_newComposeBuffer(lines, start_on_line) - let lines = a:lines - let start_on_line = a:start_on_line - let real_hdr_start = 1 - if g:notmuch_compose_header_help - let help_lines = [ - \ 'Notmuch-Help: Type in your message here; to help you use these bindings:', - \ 'Notmuch-Help: ,a - attach a file', - \ 'Notmuch-Help: ,s - send the message (Notmuch-Help lines will be removed)', - \ 'Notmuch-Help: ,q - abort the message', - \ 'Notmuch-Help: - skip through header lines', - \ ] - call extend(lines, help_lines, 0) - let real_hdr_start = len(help_lines) - if start_on_line > 0 - let start_on_line = start_on_line + len(help_lines) - endif - endif - call extend(lines, g:notmuch_signature) - - - let prev_bufnr = bufnr('%') - setlocal bufhidden=hide - call NM_newFileBuffer(g:notmuch_compose_temp_file_dir, '%s.mail', - \ 'compose', lines) - setlocal bufhidden=hide - let b:nm_prev_bufnr = prev_bufnr - - call NM_set_map('n', g:notmuch_compose_nmaps) - call NM_set_map('i', g:notmuch_compose_imaps) - - if start_on_line > 0 && start_on_line <= len(lines) - call cursor(start_on_line, strlen(getline(start_on_line)) + 1) - else - call cursor(real_hdr_start, strlen(getline(real_hdr_start)) + 1) - call NM_compose_next_entry_area() - endif - - if g:notmuch_compose_insert_mode_start - startinsert! - endif - echo 'Type your message, use to jump to next header and then body.' -endfunction - -function! s:NM_assert_buffer_type(type) - if !exists('b:nm_type') || b:nm_type != a:type - throw printf('Eeek! expected type %s, but got %s.', a:type, - \ exists(b:nm_type) ? b:nm_type : 'something else') - endif -endfunction - -function! s:NM_mktemp(dir, name) - let time_stamp = strftime('%Y%m%d-%H%M%S') - let file_name = substitute(a:dir,'/*$','/','') . printf(a:name, time_stamp) - " TODO: check if it exists, try again - return file_name -endfunction - -function! s:NM_shell_escape(word) - " TODO: use shellescape() - let word = substitute(a:word, '''', '\\''', 'g') - return '''' . word . '''' -endfunction - -" this function was taken from git.vim, then fixed up -" https://github.com/motemen/git-vim -function! s:NM_shell_split(cmd) - let l:split_cmd = [] - let cmd = a:cmd - let iStart = 0 - while 1 - let t = match(cmd, '\S', iStart) - if t < iStart - break - endif - let iStart = t - - let iSpace = match(cmd, '\v(\s|$)', iStart) - if iSpace < iStart - break - endif - - let iQuote1 = match(cmd, '\(^["'']\|[^\\]\@<=["'']\)', iStart) - if iQuote1 > iSpace || iQuote1 < iStart - let iEnd = iSpace - 1 - let l:split_cmd += [ cmd[iStart : iEnd] ] - else - let q = cmd[iQuote1] - let iQuote2 = match(cmd, '[^\\]\@<=[' . q . ']', iQuote1 + 1) - if iQuote2 < iQuote1 - throw 'No matching ' . q . ' quote' - endif - let iEnd = iQuote2 - let l:split_cmd += [ cmd[iStart+1 : iEnd-1 ] ] - endif - - - let iStart = iEnd + 1 - endwhile - - return l:split_cmd -endfunction - - -function! s:NM_run(args) - let words = a:args - call map(words, 's:NM_shell_escape(v:val)') - let cmd = g:notmuch_cmd . ' ' . join(words) . '< /dev/null' - - if exists('g:notmuch_debug') && g:notmuch_debug - let start = reltime() - let out = system(cmd) - let err = v:shell_error - let delta = reltime(start) - - echo printf('[%s] {%s} %s', reltimestr(delta), string(err), string(cmd)) - else - let out = system(cmd) - let err = v:shell_error - endif - - if err - echohl Error - echo substitute(out, '\n*$', '', '') - echohl None - return '' - else - return out - endif -endfunction - -" --- external mail handling helpers {{{1 - -function! s:NM_new_mail() - call NM_cmd_compose([], []) -endfunction - -" --- tag manipulation helpers {{{1 - -" used to combine an array of words with prefixes and separators -" example: -" NM_combine_tags('tag:', ['one', 'two', 'three'], 'OR', '()') -" -> ['(', 'tag:one', 'OR', 'tag:two', 'OR', 'tag:three', ')'] -function! s:NM_combine_tags(word_prefix, words, separator, brackets) - let res = [] - for word in a:words - if len(res) && strlen(a:separator) - call add(res, a:separator) - endif - call add(res, a:word_prefix . word) - endfor - if len(res) > 1 && strlen(a:brackets) - if strlen(a:brackets) != 2 - throw 'Eeek! brackets arg to NM_combine_tags must be 2 chars' - endif - call insert(res, a:brackets[0]) - call add(res, a:brackets[1]) - endif - return res -endfunction - -" --- other helpers {{{1 - -function! s:NM_get_search_words() - if !exists('b:nm_search_words') - throw 'Eeek! no b:nm_search_words' - endif - return b:nm_search_words -endfunction - -function! s:NM_kill_this_buffer() - if exists('b:nm_prev_bufnr') - let prev_bufnr = b:nm_prev_bufnr - bdelete! - exec printf("buffer %d", prev_bufnr) - else - echo "This is the last buffer; use :q to quit." - endif -endfunction - -function! s:NM_search_expand(arg) - let word = expand(a:arg) - let prev_bufnr = bufnr('%') - setlocal bufhidden=hide - call NM_cmd_search([word]) - setlocal bufhidden=delete - let b:nm_prev_bufnr = prev_bufnr -endfunction - -function! s:NM_tag(filter, tags) - let filter = len(a:filter) ? a:filter : [NM_search_thread_id()] - if !len(filter) - throw 'Eeek! I couldn''t find the thread id!' - endif - let args = ['tag'] - call extend(args, a:tags) - call add(args, '--') - call extend(args, filter) - " TODO: handle errors - call NM_run(args) -endfunction - -" --- process and set the defaults {{{1 - -function! NM_set_defaults(force) - for [key, dflt] in items(s:notmuch_defaults) - let cmd = '' - if !a:force && exists(key) && type(dflt) == type(eval(key)) - continue - elseif type(dflt) == type(0) - let cmd = printf('let %s = %d', key, dflt) - elseif type(dflt) == type('') - let cmd = printf('let %s = ''%s''', key, dflt) - " FIXME: not sure why this didn't work when dflt is an array - "elseif type(dflt) == type([]) - " let cmd = printf('let %s = %s', key, string(dflt)) - else - echoe printf('E: Unknown type in NM_set_defaults(%d) using [%s,%s]', - \ a:force, key, string(dflt)) - continue - endif - exec cmd - endfor -endfunction -call NM_set_defaults(0) - -" for some reason NM_set_defaults() didn't work for arrays... -if !exists('g:notmuch_show_headers') - let g:notmuch_show_headers = s:notmuch_show_headers_defaults -endif -if !exists('g:notmuch_initial_search_words') - let g:notmuch_initial_search_words = s:notmuch_initial_search_words_defaults -endif -if !exists('g:notmuch_folders') - let g:notmuch_folders = s:notmuch_folders_defaults -endif - -if !exists('g:notmuch_signature') - let g:notmuch_signature = s:notmuch_signature_defaults -endif -if !exists('g:notmuch_compose_headers') - let g:notmuch_compose_headers = s:notmuch_compose_headers_defaults -endif - -" --- assign keymaps {{{1 - -function! s:NM_set_map(type, maps) - nmapclear - for [key, code] in items(a:maps) - exec printf('%snoremap %s %s', a:type, key, code) - endfor - " --- this is a hack for development :) - nnoremap ,nmr :runtime! plugin/notmuch.vim -endfunction - -" --- command handler {{{1 - -function! NotMuch(args) - let args = a:args - if !strlen(args) - let args = 'folders' - endif - - let words = NM_shell_split(args) - if words[0] == 'folders' || words[0] == 'f' - let words = words[1:] - call NM_cmd_folders(words) - - elseif words[0] == 'search' || words[0] == 's' - if len(words) > 1 - let words = words[1:] - elseif exists('b:nm_search_words') - let words = b:nm_search_words - else - let words = g:notmuch_initial_search_words - endif - call NM_cmd_search(words) - - elseif words[0] == 'show' - echoe 'show is not yet implemented.' - - elseif words[0] == 'new' || words[0] == 'compose' - let words = words[1:] - call NM_cmd_compose(words, []) - endif -endfunction -function! CompleteNotMuch(arg_lead, cmd_line, cursor_pos) - return [] -endfunction - - -" --- glue {{{1 - -command! -nargs=* -complete=customlist,CompleteNotMuch NotMuch call NotMuch() -cabbrev notmuch =(getcmdtype()==':' && getcmdpos()==1 ? 'NotMuch' : 'notmuch') - -" vim: set ft=vim ts=8 sw=8 et foldmethod=marker : diff --git a/contrib/notmuch-vim/syntax/notmuch-compose.vim b/contrib/notmuch-vim/syntax/notmuch-compose.vim deleted file mode 100644 index 19adb756..00000000 --- a/contrib/notmuch-vim/syntax/notmuch-compose.vim +++ /dev/null @@ -1,7 +0,0 @@ -runtime! syntax/mail.vim - -syntax region nmComposeHelp contains=nmComposeHelpLine start='^Notmuch-Help:\%1l' end='^\(Notmuch-Help:\)\@!' -syntax match nmComposeHelpLine /Notmuch-Help:/ contained - -highlight link nmComposeHelp Include -highlight link nmComposeHelpLine Error diff --git a/contrib/notmuch-vim/syntax/notmuch-folders.vim b/contrib/notmuch-vim/syntax/notmuch-folders.vim deleted file mode 100644 index 9477f86f..00000000 --- a/contrib/notmuch-vim/syntax/notmuch-folders.vim +++ /dev/null @@ -1,12 +0,0 @@ -" notmuch folders mode syntax file - -syntax region nmFoldersCount start='^' end='\%10v' -syntax region nmFoldersName start='\%11v' end='\%31v' -syntax match nmFoldersSearch /([^()]\+)$/ - -highlight link nmFoldersCount Statement -highlight link nmFoldersName Type -highlight link nmFoldersSearch String - -highlight CursorLine term=reverse cterm=reverse gui=reverse - diff --git a/contrib/notmuch-vim/syntax/notmuch-git-diff.vim b/contrib/notmuch-vim/syntax/notmuch-git-diff.vim deleted file mode 100644 index 6f15fdc7..00000000 --- a/contrib/notmuch-vim/syntax/notmuch-git-diff.vim +++ /dev/null @@ -1,26 +0,0 @@ -syn match diffRemoved "^-.*" -syn match diffAdded "^+.*" - -syn match diffSeparator "^---$" -syn match diffSubname " @@..*"ms=s+3 contained -syn match diffLine "^@.*" contains=diffSubname - -syn match diffFile "^diff .*" -syn match diffNewFile "^+++ .*" -syn match diffOldFile "^--- .*" - -hi def link diffOldFile diffFile -hi def link diffNewFile diffFile - -hi def link diffFile Type -hi def link diffRemoved Special -hi def link diffAdded Identifier -hi def link diffLine Statement -hi def link diffSubname PreProc - -syntax match gitDiffStatLine /^ .\{-}\zs[+-]\+$/ contains=gitDiffStatAdd,gitDiffStatDelete -syntax match gitDiffStatAdd /+/ contained -syntax match gitDiffStatDelete /-/ contained - -hi def link gitDiffStatAdd diffAdded -hi def link gitDiffStatDelete diffRemoved diff --git a/contrib/notmuch-vim/syntax/notmuch-search.vim b/contrib/notmuch-vim/syntax/notmuch-search.vim deleted file mode 100644 index f458d778..00000000 --- a/contrib/notmuch-vim/syntax/notmuch-search.vim +++ /dev/null @@ -1,12 +0,0 @@ -syntax region nmSearch start=/^/ end=/$/ oneline contains=nmSearchDate -syntax match nmSearchDate /^.\{-13}/ contained nextgroup=nmSearchNum -syntax match nmSearchNum /.\{-4}/ contained nextgroup=nmSearchFrom -syntax match nmSearchFrom /.\{-21}/ contained nextgroup=nmSearchSubject -syntax match nmSearchSubject /.\{0,}\(([^()]\+)$\)\@=/ contained nextgroup=nmSearchTags -syntax match nmSearchTags /.\+$/ contained - -highlight link nmSearchDate Statement -highlight link nmSearchNum Type -highlight link nmSearchFrom Include -highlight link nmSearchSubject Normal -highlight link nmSearchTags String diff --git a/contrib/notmuch-vim/syntax/notmuch-show.vim b/contrib/notmuch-vim/syntax/notmuch-show.vim deleted file mode 100644 index c3a98b77..00000000 --- a/contrib/notmuch-vim/syntax/notmuch-show.vim +++ /dev/null @@ -1,24 +0,0 @@ -" notmuch show mode syntax file - -syntax cluster nmShowMsgDesc contains=nmShowMsgDescWho,nmShowMsgDescDate,nmShowMsgDescTags -syntax match nmShowMsgDescWho /[^)]\+)/ contained -syntax match nmShowMsgDescDate / ([^)]\+[0-9]) / contained -syntax match nmShowMsgDescTags /([^)]\+)$/ contained - -syntax cluster nmShowMsgHead contains=nmShowMsgHeadKey,nmShowMsgHeadVal -syntax match nmShowMsgHeadKey /^[^:]\+: / contained -syntax match nmShowMsgHeadVal /^\([^:]\+: \)\@<=.*/ contained - -syntax cluster nmShowMsgBody contains=@nmShowMsgBodyMail,@nmShowMsgBodyGit -syntax include @nmShowMsgBodyMail syntax/mail.vim - -silent! syntax include @nmShowMsgBodyGit syntax/notmuch-git-diff.vim - -highlight nmShowMsgDescWho term=reverse cterm=reverse gui=reverse -highlight link nmShowMsgDescDate Type -highlight link nmShowMsgDescTags String - -highlight link nmShowMsgHeadKey Macro -"highlight link nmShowMsgHeadVal NONE - -highlight Folded term=reverse ctermfg=LightGrey ctermbg=Black guifg=LightGray guibg=Black From b0d03fc5ba1211ed783a5c052d01407d95c8446f Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 23 Aug 2016 20:50:05 -0300 Subject: [PATCH 084/179] bindings: move go bindings to contrib This signals two things, an intent to be more liberal about accepting patches, and an intent to stop distributing the bindings if maintenance doesn't pick up. --- {bindings => contrib}/go/.gitignore | 0 {bindings => contrib}/go/LICENSE | 0 {bindings => contrib}/go/Makefile | 0 {bindings => contrib}/go/README | 0 {bindings => contrib}/go/src/notmuch-addrlookup/addrlookup.go | 0 {bindings => contrib}/go/src/notmuch/notmuch.go | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename {bindings => contrib}/go/.gitignore (100%) rename {bindings => contrib}/go/LICENSE (100%) rename {bindings => contrib}/go/Makefile (100%) rename {bindings => contrib}/go/README (100%) rename {bindings => contrib}/go/src/notmuch-addrlookup/addrlookup.go (100%) rename {bindings => contrib}/go/src/notmuch/notmuch.go (100%) diff --git a/bindings/go/.gitignore b/contrib/go/.gitignore similarity index 100% rename from bindings/go/.gitignore rename to contrib/go/.gitignore diff --git a/bindings/go/LICENSE b/contrib/go/LICENSE similarity index 100% rename from bindings/go/LICENSE rename to contrib/go/LICENSE diff --git a/bindings/go/Makefile b/contrib/go/Makefile similarity index 100% rename from bindings/go/Makefile rename to contrib/go/Makefile diff --git a/bindings/go/README b/contrib/go/README similarity index 100% rename from bindings/go/README rename to contrib/go/README diff --git a/bindings/go/src/notmuch-addrlookup/addrlookup.go b/contrib/go/src/notmuch-addrlookup/addrlookup.go similarity index 100% rename from bindings/go/src/notmuch-addrlookup/addrlookup.go rename to contrib/go/src/notmuch-addrlookup/addrlookup.go diff --git a/bindings/go/src/notmuch/notmuch.go b/contrib/go/src/notmuch/notmuch.go similarity index 100% rename from bindings/go/src/notmuch/notmuch.go rename to contrib/go/src/notmuch/notmuch.go From 263a690ac47ced487a7305bd5056723662d1c28c Mon Sep 17 00:00:00 2001 From: laochailan Date: Sun, 31 May 2015 14:02:11 +0200 Subject: [PATCH 085/179] Added thread bindings to go bindings --- contrib/go/src/notmuch/notmuch.go | 262 +++++++++++++++++++++++++++++- 1 file changed, 260 insertions(+), 2 deletions(-) diff --git a/contrib/go/src/notmuch/notmuch.go b/contrib/go/src/notmuch/notmuch.go index 4d8c2ceb..a8611ebb 100644 --- a/contrib/go/src/notmuch/notmuch.go +++ b/contrib/go/src/notmuch/notmuch.go @@ -534,8 +534,6 @@ func (self *Query) CountMessages() uint { return uint(C.notmuch_query_count_messages(self.query)) } -// TODO: wrap threads and thread - /* Is the given 'threads' iterator pointing at a valid thread. * * When this function returns TRUE, notmuch_threads_get will return a @@ -556,6 +554,45 @@ func (self *Threads) Valid() bool { return true } +/* Get the current thread from 'threads' as a notmuch_thread_t. + * + * Note: The returned thread belongs to 'threads' and has a lifetime + * identical to it (and the query to which it belongs). + * + * See the documentation of notmuch_query_search_threads for example + * code showing how to iterate over a notmuch_threads_t object. + * + * If an out-of-memory situation occurs, this function will return + * NULL. + */ +func (self *Threads) Get() *Thread { + if self.threads == nil { + return nil + } + thread := C.notmuch_threads_get(self.threads) + if thread == nil { + return nil + } + return &Thread{thread} +} + +/* Move the 'threads' iterator to the next thread. + * + * If 'threads' is already pointing at the last thread then the + * iterator will be moved to a point just beyond that last thread, + * (where notmuch_threads_valid will return FALSE and + * notmuch_threads_get will return NULL). + * + * See the documentation of notmuch_query_search_threads for example + * code showing how to iterate over a notmuch_threads_t object. + */ +func (self *Threads) MoveToNext() { + if self.threads == nil { + return + } + C.notmuch_threads_move_to_next(self.threads) +} + /* Destroy a notmuch_threads_t object. * * It's not strictly necessary to call this function. All memory from @@ -568,6 +605,227 @@ func (self *Threads) Destroy() { } } +/** + * Get the thread ID of 'thread'. + * + * The returned string belongs to 'thread' and as such, should not be + * modified by the caller and will only be valid for as long as the + * thread is valid, (which is until notmuch_thread_destroy or until + * the query from which it derived is destroyed). + */ +func (self *Thread) GetThreadId() string { + if self.thread == nil { + return "" + } + id := C.notmuch_thread_get_thread_id(self.thread) + if id == nil { + return "" + } + return C.GoString(id) +} + +/** + * Get the total number of messages in 'thread'. + * + * This count consists of all messages in the database belonging to + * this thread. Contrast with notmuch_thread_get_matched_messages() . + */ +func (self *Thread) GetTotalMessages() int { + if self.thread == nil { + return 0 + } + return int(C.notmuch_thread_get_total_messages(self.thread)) +} + +/** + * Get a notmuch_messages_t iterator for the top-level messages in + * 'thread' in oldest-first order. + * + * This iterator will not necessarily iterate over all of the messages + * in the thread. It will only iterate over the messages in the thread + * which are not replies to other messages in the thread. + * + * The returned list will be destroyed when the thread is destroyed. + */ +func (self *Thread) GetToplevelMessages() (*Messages, Status) { + if self.thread == nil { + return nil, STATUS_NULL_POINTER + } + + msgs := C.notmuch_thread_get_toplevel_messages(self.thread) + if msgs == nil { + return nil, STATUS_NULL_POINTER + } + return &Messages{msgs}, STATUS_SUCCESS +} + +/** + * Get a notmuch_thread_t iterator for all messages in 'thread' in + * oldest-first order. + * + * The returned list will be destroyed when the thread is destroyed. + */ +func (self *Thread) GetMessages() (*Messages, Status) { + if self.thread == nil { + return nil, STATUS_NULL_POINTER + } + + msgs := C.notmuch_thread_get_messages(self.thread) + if msgs == nil { + return nil, STATUS_NULL_POINTER + } + return &Messages{msgs}, STATUS_SUCCESS +} + +/** + * Get the number of messages in 'thread' that matched the search. + * + * This count includes only the messages in this thread that were + * matched by the search from which the thread was created and were + * not excluded by any exclude tags passed in with the query (see + * notmuch_query_add_tag_exclude). Contrast with + * notmuch_thread_get_total_messages() . + */ +func (self *Thread) GetMatchedMessages() int { + if self.thread == nil { + return 0 + } + return int(C.notmuch_thread_get_matched_messages(self.thread)) +} + +/** + * Get the authors of 'thread' as a UTF-8 string. + * + * The returned string is a comma-separated list of the names of the + * authors of mail messages in the query results that belong to this + * thread. + * + * The string contains authors of messages matching the query first, then + * non-matched authors (with the two groups separated by '|'). Within + * each group, authors are ordered by date. + * + * The returned string belongs to 'thread' and as such, should not be + * modified by the caller and will only be valid for as long as the + * thread is valid, (which is until notmuch_thread_destroy or until + * the query from which it derived is destroyed). + */ +func (self *Thread) GetAuthors() string { + if self.thread == nil { + return "" + } + str := C.notmuch_thread_get_authors(self.thread) + if str == nil { + return "" + } + return C.GoString(str) +} + +/** + * Get the subject of 'thread' as a UTF-8 string. + * + * The subject is taken from the first message (according to the query + * order---see notmuch_query_set_sort) in the query results that + * belongs to this thread. + * + * The returned string belongs to 'thread' and as such, should not be + * modified by the caller and will only be valid for as long as the + * thread is valid, (which is until notmuch_thread_destroy or until + * the query from which it derived is destroyed). + */ +func (self *Thread) GetSubject() string { + if self.thread == nil { + return "" + } + str := C.notmuch_thread_get_subject(self.thread) + if str == nil { + return "" + } + return C.GoString(str) +} + +/** + * Get the date of the oldest message in 'thread' as a time_t value. + */ +func (self *Thread) GetOldestDate() int64 { + if self.thread == nil { + return 0 + } + date := C.notmuch_thread_get_oldest_date(self.thread) + + return int64(date) +} + +/** + * Get the date of the newest message in 'thread' as a time_t value. + */ +func (self *Thread) GetNewestDate() int64 { + if self.thread == nil { + return 0 + } + date := C.notmuch_thread_get_newest_date(self.thread) + + return int64(date) +} + +/** + * Get the tags for 'thread', returning a notmuch_tags_t object which + * can be used to iterate over all tags. + * + * Note: In the Notmuch database, tags are stored on individual + * messages, not on threads. So the tags returned here will be all + * tags of the messages which matched the search and which belong to + * this thread. + * + * The tags object is owned by the thread and as such, will only be + * valid for as long as the thread is valid, (for example, until + * notmuch_thread_destroy or until the query from which it derived is + * destroyed). + * + * Typical usage might be: + * + * notmuch_thread_t *thread; + * notmuch_tags_t *tags; + * const char *tag; + * + * thread = notmuch_threads_get (threads); + * + * for (tags = notmuch_thread_get_tags (thread); + * notmuch_tags_valid (tags); + * notmuch_tags_move_to_next (tags)) + * { + * tag = notmuch_tags_get (tags); + * .... + * } + * + * notmuch_thread_destroy (thread); + * + * Note that there's no explicit destructor needed for the + * notmuch_tags_t object. (For consistency, we do provide a + * notmuch_tags_destroy function, but there's no good reason to call + * it if the message is about to be destroyed). + */ +func (self *Thread) GetTags() *Tags { + if self.thread == nil { + return nil + } + + tags := C.notmuch_thread_get_tags(self.thread) + if tags == nil { + return nil + } + + return &Tags{tags} +} + +/** + * Destroy a notmuch_thread_t object. + */ +func (self *Thread) Destroy() { + if self.thread != nil { + C.notmuch_thread_destroy(self.thread) + } +} + /* Is the given 'messages' iterator pointing at a valid message. * * When this function returns TRUE, notmuch_messages_get will return a From a26b06f09dd2317ab8ffb76dcc1ebb64ff2a80ad Mon Sep 17 00:00:00 2001 From: laochailan Date: Sun, 31 May 2015 14:02:12 +0200 Subject: [PATCH 086/179] fixed wrong constant values before, they were both zero, so getting a read-writeable handle was impossible. --- contrib/go/src/notmuch/notmuch.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/go/src/notmuch/notmuch.go b/contrib/go/src/notmuch/notmuch.go index a8611ebb..2d684311 100644 --- a/contrib/go/src/notmuch/notmuch.go +++ b/contrib/go/src/notmuch/notmuch.go @@ -86,7 +86,7 @@ type Filenames struct { type DatabaseMode C.notmuch_database_mode_t const ( - DATABASE_MODE_READ_ONLY DatabaseMode = 0 + DATABASE_MODE_READ_ONLY DatabaseMode = iota DATABASE_MODE_READ_WRITE ) From 81045d9527f06d6a7c18854c73e09e539192d7c6 Mon Sep 17 00:00:00 2001 From: laochailan Date: Sun, 31 May 2015 14:02:15 +0200 Subject: [PATCH 087/179] updated NEWS --- NEWS | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/NEWS b/NEWS index 3a9c8d36..74beb2c7 100644 --- a/NEWS +++ b/NEWS @@ -598,6 +598,20 @@ Python Bindings Add support for `notmuch_query_add_tag_exclude` +Go Bindings +----------- + +Add support for `notmuch_threads_t` and `notmuch_thread_t` + +Fixed constant values so they are not all zero anymore. + + Previously, it was impossible to open writable database handles, + because DATABASE_MODE_READ_ONLY and DATABASE_MODE_READ_WRITE were + both set to zero. + The same issue occured with sort modes. + +Formatting improvements for Godoc generated documentation + Build System ------------ From 7352cadb4f2e48890a6692b5ac6783cce0e50808 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 4 Sep 2016 08:04:48 -0300 Subject: [PATCH 088/179] NEWS: fix go bindings NEWS Move to correct release. Mention moving to contrib. Drop mention of formatting fixes, as those are not yet applied. --- NEWS | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/NEWS b/NEWS index 74beb2c7..02214143 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,20 @@ Ruby Bindings Add support for `notmuch_database_get_all_tags` +Go Bindings +----------- + +Go bindings moved to contrib + +Add support for `notmuch_threads_t` and `notmuch_thread_t` + +Fixed constant values so they are not all zero anymore. + + Previously, it was impossible to open writable database handles, + because DATABASE_MODE_READ_ONLY and DATABASE_MODE_READ_WRITE were + both set to zero. + The same issue occured with sort modes. + Notmuch 0.22.1 (2016-07-19) =========================== @@ -598,20 +612,6 @@ Python Bindings Add support for `notmuch_query_add_tag_exclude` -Go Bindings ------------ - -Add support for `notmuch_threads_t` and `notmuch_thread_t` - -Fixed constant values so they are not all zero anymore. - - Previously, it was impossible to open writable database handles, - because DATABASE_MODE_READ_ONLY and DATABASE_MODE_READ_WRITE were - both set to zero. - The same issue occured with sort modes. - -Formatting improvements for Godoc generated documentation - Build System ------------ From 2cf0ef3998a75a8e5bcd83f0a286d9660f1bdb0e Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Fri, 20 May 2016 21:13:02 +0100 Subject: [PATCH 089/179] emacs: address completion, allow sender/recipient and filters This commit lets the user customize the address completion. It makes two changes. The first change controls whether to build the address completion list based on messages you have sent or you have received (the latter is much faster). The second change add a possible filter query to limit the messages used -- for example, setting this to date:1y.. would limit the address completions to addresses used in the last year. This speeds up the address harvest and may also make the search less cluttered as old addresses may well no longer be valid. --- emacs/notmuch-address.el | 135 ++++++++++++++++++++++++++++----------- emacs/notmuch-company.el | 2 +- emacs/notmuch-mua.el | 3 +- 3 files changed, 98 insertions(+), 42 deletions(-) diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el index d372fbaf..c6f5f111 100644 --- a/emacs/notmuch-address.el +++ b/emacs/notmuch-address.el @@ -28,15 +28,62 @@ ;; (declare-function company-manual-begin "company") +(defvar notmuch-address-last-harvest 0 + "Time of last address harvest") + +(defvar notmuch-address-completions (make-hash-table :test 'equal) + "Hash of email addresses for completion during email composition. + This variable is set by calling `notmuch-address-harvest'.") + +(defvar notmuch-address-full-harvest-finished nil + "t indicates that full completion address harvesting has been +finished") + (defcustom notmuch-address-command 'internal - "The command which generates possible addresses. It must take a -single argument and output a list of possible matches, one per -line. The default value of `internal' uses built-in address -completion." + "Determines how address completion candidates are generated. + +If it is a string then that string should be an external program +which must take a single argument (searched string) and output a +list of completion candidates, one per line. + +Alternatively, it can be the symbol 'internal, in which case +internal completion is used; the variable +`notmuch-address-internal-completion` can be used to customize +this case. + +Finally, if this variable is nil then address completion is +disabled." :type '(radio (const :tag "Use internal address completion" internal) (const :tag "Disable address completion" nil) - (string :tag "Use external completion command" "notmuch-addresses")) + (string :tag "Use external completion command")) + :group 'notmuch-send + :group 'notmuch-external) + +(defcustom notmuch-address-internal-completion '(sent nil) + "Determines how internal address completion generates candidates. + +This should be a list of the form '(DIRECTION FILTER), where + DIRECTION is either sent or received and specifies whether the + candidates are searched in messages sent by the user or received + by the user (note received by is much faster), and FILTER is + either nil or a filter-string, such as \"date:1y..\" to append + to the query." + :type '(list :tag "Use internal address completion" + (radio + :tag "Base completion on messages you have" + :value sent + (const :tag "sent (more accurate)" sent) + (const :tag "received (faster)" received)) + (radio :tag "Filter messages used for completion" + (const :tag "Use all messages" nil) + (string :tag "Filter query"))) + ;; We override set so that we can clear the cache when this changes + :set (lambda (symbol value) + (set-default symbol value) + (setq notmuch-address-last-harvest 0) + (setq notmuch-address-completions (clrhash notmuch-address-completions)) + (setq notmuch-address-full-harvest-finished nil)) :group 'notmuch-send :group 'notmuch-external) @@ -51,17 +98,6 @@ to know how address selection is made by default." :group 'notmuch-send :group 'notmuch-external) -(defvar notmuch-address-last-harvest 0 - "Time of last address harvest") - -(defvar notmuch-address-completions (make-hash-table :test 'equal) - "Hash of email addresses for completion during email composition. - This variable is set by calling `notmuch-address-harvest'.") - -(defvar notmuch-address-full-harvest-finished nil - "t indicates that full completion address harvesting has been -finished") - (defun notmuch-address-selection-function (prompt collection initial-input) "Call (`completing-read' PROMPT COLLECTION nil nil INITIAL-INPUT 'notmuch-address-history)" @@ -86,9 +122,7 @@ finished") (eq notmuch-address-command 'internal) (require 'company nil t))) (pair (cons notmuch-address-completion-headers-regexp - (if use-company - #'company-manual-begin - #'notmuch-address-expand-name)))) + #'notmuch-address-expand-name))) (when use-company (notmuch-company-setup)) (unless (memq pair message-completion-alist) @@ -115,7 +149,7 @@ external commands." (when (not notmuch-address-full-harvest-finished) ;; First, run quick synchronous harvest based on what the user ;; entered so far - (notmuch-address-harvest (format "to:%s*" original) t)) + (notmuch-address-harvest original t)) (prog1 (notmuch-address-matching original) ;; Then start the (potentially long-running) full asynchronous harvest if necessary (notmuch-address-harvest-trigger))) @@ -123,7 +157,12 @@ external commands." (process-lines notmuch-address-command original)))) (defun notmuch-address-expand-name () - (when notmuch-address-command + (cond + ((and (eq notmuch-address-command 'internal) + notmuch-address-use-company + (bound-and-true-p company-mode)) + (company-manual-begin)) + (notmuch-address-command (let* ((end (point)) (beg (save-excursion (re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*") @@ -149,7 +188,8 @@ external commands." (delete-region beg end) (insert chosen)) (message "No matches.") - (ding))))) + (ding)))) + (t nil))) ;; Copied from `w3m-which-command'. (defun notmuch-address-locate-command (command) @@ -191,32 +231,49 @@ external commands." The car is a partial harvest, and the cdr is a full harvest") -(defun notmuch-address-harvest (&optional filter-query synchronous callback) - "Collect addresses completion candidates. It queries the -notmuch database for all messages sent by the user optionally -matching FILTER-QUERY (if not nil). It collects the destination -addresses from those messages and stores them in -`notmuch-address-completions'. Address harvesting may take some -time so the address collection runs asynchronously unless -SYNCHRONOUS is t. In case of asynchronous execution, CALLBACK is -called when harvesting finishes." - (let* ((from-me-query (mapconcat (lambda (x) (concat "from:" x)) (notmuch-user-emails) " or ")) - (query (if filter-query - (format "(%s) and (%s)" from-me-query filter-query) - from-me-query)) +(defun notmuch-address-harvest (&optional addr-prefix synchronous callback) + "Collect addresses completion candidates. + +It queries the notmuch database for messages sent/received (as +configured with `notmuch-address-command`) by the user, collects +destination/source addresses from those messages and stores them +in `notmuch-address-completions'. + +If ADDR-PREFIX is not nil, only messages with to/from addresses +matching ADDR-PREFIX*' are queried. + +Address harvesting may take some time so the address collection runs +asynchronously unless SYNCHRONOUS is t. In case of asynchronous +execution, CALLBACK is called when harvesting finishes." + + (let* ((sent (eq (car notmuch-address-internal-completion) 'sent)) + (config-query (cadr notmuch-address-internal-completion)) + (prefix-query (when addr-prefix + (format "%s:%s*" (if sent "to" "from") addr-prefix))) + (from-or-to-me-query + (mapconcat (lambda (x) + (concat (if sent "from:" "to:") x)) + (notmuch-user-emails) " or ")) + (query (if (or prefix-query config-query) + (concat (format "(%s)" from-or-to-me-query) + (when prefix-query + (format " and (%s)" prefix-query)) + (when config-query + (format " and (%s)" config-query))) + from-or-to-me-query)) (args `("address" "--format=sexp" "--format-version=2" - "--output=recipients" + ,(if sent "--output=recipients" "--output=sender") "--deduplicate=address" ,query))) (if synchronous (mapc #'notmuch-address-harvest-addr (apply 'notmuch-call-notmuch-sexp args)) ;; Asynchronous - (let* ((current-proc (if filter-query + (let* ((current-proc (if addr-prefix (car notmuch-address-harvest-procs) (cdr notmuch-address-harvest-procs))) (proc-name (format "notmuch-address-%s-harvest" - (if filter-query "partial" "full"))) + (if addr-prefix "partial" "full"))) (proc-buf (concat " *" proc-name "*"))) ;; Kill any existing process (when current-proc @@ -228,7 +285,7 @@ called when harvesting finishes." args)) (set-process-filter current-proc 'notmuch-address-harvest-filter) (set-process-query-on-exit-flag current-proc nil) - (if filter-query + (if addr-prefix (setcar notmuch-address-harvest-procs current-proc) (setcdr notmuch-address-harvest-procs current-proc))))) ;; return value diff --git a/emacs/notmuch-company.el b/emacs/notmuch-company.el index 0619c7ef..610e223d 100644 --- a/emacs/notmuch-company.el +++ b/emacs/notmuch-company.el @@ -72,7 +72,7 @@ (lambda (callback) ;; First run quick asynchronous harvest based on what the user entered so far (notmuch-address-harvest - (format "to:%s*" arg) nil + arg nil (lambda (_proc _event) (funcall callback (notmuch-address-matching arg)) ;; Then start the (potentially long-running) full asynchronous harvest if necessary diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index f3a4e5a3..500d6638 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -276,8 +276,7 @@ mutiple parts get a header." (define-derived-mode notmuch-message-mode message-mode "Message[Notmuch]" "Notmuch message composition mode. Mostly like `message-mode'" - (when notmuch-address-command - (notmuch-address-setup))) + (notmuch-address-setup)) (put 'notmuch-message-mode 'flyspell-mode-predicate 'mail-mode-flyspell-verify) From 827c28a04effc58f29e751788690d2f4b434902d Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Fri, 20 May 2016 21:13:03 +0100 Subject: [PATCH 090/179] emacs: address: allow internal completion on an individual basis This commit makes two changes. The first allows the user to override an external completion method with the internal notmuch address based completion for an individual buffer. Secondly, if the user has company-mode enabled then it sets up company mode (based on internal completion) but disables the automatic timeout completion -- the user can still activate it in when desired with standard company commands such as company-complete. --- emacs/notmuch-address.el | 19 ++++++++++++++++--- emacs/notmuch-company.el | 8 +++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el index c6f5f111..10eaab19 100644 --- a/emacs/notmuch-address.el +++ b/emacs/notmuch-address.el @@ -118,17 +118,30 @@ to know how address selection is made by default." :group 'notmuch-send) (defun notmuch-address-setup () - (let* ((use-company (and notmuch-address-use-company - (eq notmuch-address-command 'internal) + (let* ((setup-company (and notmuch-address-use-company (require 'company nil t))) (pair (cons notmuch-address-completion-headers-regexp #'notmuch-address-expand-name))) - (when use-company + (when setup-company (notmuch-company-setup)) (unless (memq pair message-completion-alist) (setq message-completion-alist (push pair message-completion-alist))))) +(defun notmuch-address-toggle-internal-completion () + "Toggle use of internal completion for current buffer. + +This overrides the global setting for address completion and +toggles the setting in this buffer." + (interactive) + (if (local-variable-p 'notmuch-address-command) + (kill-local-variable 'notmuch-address-command) + (setq-local notmuch-address-command 'internal)) + (if (boundp 'company-idle-delay) + (if (local-variable-p 'company-idle-delay) + (kill-local-variable 'company-idle-delay) + (setq-local company-idle-delay nil)))) + (defun notmuch-address-matching (substring) "Returns a list of completion candidates matching SUBSTRING. The candidates are taken from `notmuch-address-completions'." diff --git a/emacs/notmuch-company.el b/emacs/notmuch-company.el index 610e223d..168315ff 100644 --- a/emacs/notmuch-company.el +++ b/emacs/notmuch-company.el @@ -47,7 +47,13 @@ (defun notmuch-company-setup () (company-mode) (make-local-variable 'company-backends) - (setq company-backends '(notmuch-company))) + (setq company-backends '(notmuch-company)) + ;; Disable automatic company completion unless an internal + ;; completion method is configured. Company completion (using + ;; internal completion) can still be accessed via standard company + ;; functions, e.g., company-complete. + (unless (eq notmuch-address-command 'internal) + (setq-local company-idle-delay nil))) ;;;###autoload (defun notmuch-company (command &optional arg &rest _ignore) From 17341ec798b4cb2fe192824bcf8b7e78236e4121 Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Thu, 26 May 2016 00:04:31 +0300 Subject: [PATCH 091/179] Makefile.local: make IS_GIT simply expanded By using ':=' while setting IS_GIT it is expanded to 'yes' or 'no' at that point (and not every time when $(IS_GIT) is referenced). --- Makefile.local | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.local b/Makefile.local index f2ad0c12..20041947 100644 --- a/Makefile.local +++ b/Makefile.local @@ -10,7 +10,7 @@ # repository), we let git append identification of the actual commit. PACKAGE=notmuch -IS_GIT=$(shell if [ -d ${srcdir}/.git -o -f ${srcdir}/.git ] ; then echo yes ; else echo no; fi) +IS_GIT:=$(shell if [ -d ${srcdir}/.git -o -f ${srcdir}/.git ] ; then echo yes ; else echo no; fi) ifeq ($(IS_GIT),yes) DATE:=$(shell git --git-dir=${srcdir}/.git log --date=short -1 --pretty=format:%cd) From e0e6a17675a4f4f56f2c6891aaf7eb50f404ffd2 Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Thu, 26 May 2016 00:04:32 +0300 Subject: [PATCH 092/179] Makefile.local: use $(wildcard) to check existence of ${srcdir}/.git With this GNU Make construct one shell invocation can be skipped and code looks shorter (narrower). This would now match to .git being other file type than regular file or directory (or symlink to those), but that is not a use case anyone should expect users to do. --- Makefile.local | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.local b/Makefile.local index 20041947..5587cd2c 100644 --- a/Makefile.local +++ b/Makefile.local @@ -10,7 +10,7 @@ # repository), we let git append identification of the actual commit. PACKAGE=notmuch -IS_GIT:=$(shell if [ -d ${srcdir}/.git -o -f ${srcdir}/.git ] ; then echo yes ; else echo no; fi) +IS_GIT:=$(if $(wildcard ${srcdir}/.git),yes,no) ifeq ($(IS_GIT),yes) DATE:=$(shell git --git-dir=${srcdir}/.git log --date=short -1 --pretty=format:%cd) From 74b54fb0faff4a6f69f1cf54dba9ac39e9aa754c Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Sat, 3 Sep 2016 23:59:38 +0100 Subject: [PATCH 093/179] emacs: notmuch-check-exit-status bugfix This function prints diagnostic information in the event of an error. However, one of the callers has an optional :stdin-string keyword argument. This causes the error printing routine to error itself. Rather than reach notmuch-check-exit-status about the possible keyword arguments (currently only one but could be more in the future) this commit just tells notmuch-check-exit-status how to print non-string arguments. --- emacs/notmuch-lib.el | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index 1781af48..2f015b0d 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -790,9 +790,15 @@ You may need to restart Emacs or upgrade your notmuch package.")) (insert-file-contents err-file) (unless (eobp) (buffer-string))))) + (command-string + (mapconcat (lambda (arg) + (shell-quote-argument + (cond ((stringp arg) arg) + ((symbolp arg) (symbol-name arg)) + (t "*UNKNOWN ARGUMENT*")))) + command " ")) (extra - (concat - "command: " (mapconcat #'shell-quote-argument command " ") "\n" + (concat "command: " command-string "\n" (if (integerp exit-status) (format "exit status: %s\n" exit-status) (format "exit signal: %s\n" exit-status)) From 37859d1fcb1ad661666dbdb95372becaa98d46cb Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Sat, 3 Sep 2016 23:59:39 +0100 Subject: [PATCH 094/179] emacs: maildir-fcc: prepare for use of notmuch insert We move some code around in preparation for the use of notmuch insert. In particular, we move the check for a valid maildir for the fcc to when the message is sent rather than when the fcc header is inserted. The main motivation is consistency with the insert version (coming later) where we cannot check the validity until send. We allow the user some chance to correct the header; the choice here is intended to be consistent with the insert version to come. --- emacs/notmuch-maildir-fcc.el | 69 +++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/emacs/notmuch-maildir-fcc.el b/emacs/notmuch-maildir-fcc.el index bbf61320..835258f8 100644 --- a/emacs/notmuch-maildir-fcc.el +++ b/emacs/notmuch-maildir-fcc.el @@ -65,11 +65,9 @@ yet when sending a mail." :require 'notmuch-fcc-initialization :group 'notmuch-send) -(defun notmuch-fcc-handler (destdir) - "Write buffer to `destdir', marking it as sent -Intended to be dynamically bound to `message-fcc-handler-function'" - (notmuch-maildir-fcc-write-buffer-to-maildir destdir t)) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Functions which set up the fcc header in the message buffer. (defun notmuch-fcc-header-setup () "Add an Fcc header to the current message buffer. @@ -110,27 +108,25 @@ by notmuch-mua-mail" (error "Invalid `notmuch-fcc-dirs' setting (neither string nor list)"))))) (when subdir - (message-add-header - (concat "Fcc: " - (file-truename - ;; If the resulting directory is not an absolute path, - ;; prepend the standard notmuch database path. - (if (= (elt subdir 0) ?/) - subdir - (concat (notmuch-database-path) "/" subdir))))) - - ;; finally test if fcc points to a valid maildir - (let ((fcc-header (message-field-value "Fcc"))) - (unless (notmuch-maildir-fcc-dir-is-maildir-p fcc-header) - (cond ((not (file-writable-p fcc-header)) - (error (format "No permission to create %s, which does not exist" - fcc-header))) - ((y-or-n-p (format "%s is not a maildir. Create it? " - fcc-header)) - (notmuch-maildir-fcc-create-maildir fcc-header)) - (t - (error "Message not sent")))))))) - + (notmuch-maildir-add-file-style-fcc-header subdir)))) + +(defun notmuch-maildir-add-file-style-fcc-header (subdir) + (message-add-header + (concat "Fcc: " + (file-truename + ;; If the resulting directory is not an absolute path, + ;; prepend the standard notmuch database path. + (if (= (elt subdir 0) ?/) + subdir + (concat (notmuch-database-path) "/" subdir)))))) + +(defun notmuch-fcc-handler (fcc-header) + "Store message with file fcc." + (notmuch-maildir-fcc-file-fcc fcc-header)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Functions for saving a message using file fcc. + (defun notmuch-maildir-fcc-host-fixer (hostname) (replace-regexp-in-string "/\\|:" (lambda (s) @@ -192,6 +188,29 @@ if successful, nil if not." (concat destdir "/tmp/" msg-id) (concat destdir "/cur/" msg-id ":2," (when mark-seen "S")))) +(defun notmuch-maildir-fcc-file-fcc (fcc-header) + "Write the message to the file specified by FCC-HEADER. + +It offers the user a chance to correct the header, or filesystem, +if needed." + (if (notmuch-maildir-fcc-dir-is-maildir-p fcc-header) + (notmuch-maildir-fcc-write-buffer-to-maildir fcc-header 't) + ;; The fcc-header is not a valid maildir see if the user wants to + ;; fix it in some way. + (let* ((prompt (format "Fcc %s is not a maildir: (r)etry, (c)reate folder, (i)gnore, or (e)dit the header? " + fcc-header)) + (response (read-char-choice prompt '(?r ?c ?i ?e)))) + (case response + (?r (notmuch-maildir-fcc-file-fcc fcc-header)) + (?c (if (file-writable-p fcc-header) + (notmuch-maildir-fcc-create-maildir fcc-header) + (message "No permission to create %s." fcc-header) + (sit-for 2)) + (notmuch-maildir-fcc-file-fcc fcc-header)) + (?i 't) + (?e (notmuch-maildir-fcc-file-fcc + (read-from-minibuffer "Fcc header: " fcc-header))))))) + (defun notmuch-maildir-fcc-write-buffer-to-maildir (destdir &optional mark-seen) "Writes the current buffer to maildir destdir. If mark-seen is non-nil, it will write it to cur/, and mark it as read. It should From 967bbc0792d8d36cdf1e110d8b9eb0aa26d8a646 Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Sat, 3 Sep 2016 23:59:40 +0100 Subject: [PATCH 095/179] emacs: maildir import message-do-fcc We will need our own local copy of message-do-fcc so this commit just copies the code straight from message.el so that it is easier to see our local changes coming in the next commit. --- emacs/notmuch-maildir-fcc.el | 64 ++++++++++++++++++++++++++++++++++++ emacs/notmuch-mua.el | 7 ++-- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/emacs/notmuch-maildir-fcc.el b/emacs/notmuch-maildir-fcc.el index 835258f8..6fed11f2 100644 --- a/emacs/notmuch-maildir-fcc.el +++ b/emacs/notmuch-maildir-fcc.el @@ -120,6 +120,70 @@ by notmuch-mua-mail" subdir (concat (notmuch-database-path) "/" subdir)))))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Functions for saving a message either using notmuch insert or file +;; fcc. First functions common to the two cases. + +(defun notmuch-maildir-message-do-fcc () + "Process Fcc headers in the current buffer. + +This is a direct copy from message-mode's message-do-fcc." + (let ((case-fold-search t) + (buf (current-buffer)) + list file + (mml-externalize-attachments message-fcc-externalize-attachments)) + (save-excursion + (save-restriction + (message-narrow-to-headers) + (setq file (message-fetch-field "fcc" t))) + (when file + (set-buffer (get-buffer-create " *message temp*")) + (erase-buffer) + (insert-buffer-substring buf) + (message-encode-message-body) + (save-restriction + (message-narrow-to-headers) + (while (setq file (message-fetch-field "fcc" t)) + (push file list) + (message-remove-header "fcc" nil t)) + (let ((mail-parse-charset message-default-charset) + (rfc2047-header-encoding-alist + (cons '("Newsgroups" . default) + rfc2047-header-encoding-alist))) + (mail-encode-encoded-word-buffer))) + (goto-char (point-min)) + (when (re-search-forward + (concat "^" (regexp-quote mail-header-separator) "$") + nil t) + (replace-match "" t t )) + ;; Process FCC operations. + (while list + (setq file (pop list)) + (if (string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" file) + ;; Pipe the article to the program in question. + (call-process-region (point-min) (point-max) shell-file-name + nil nil nil shell-command-switch + (match-string 1 file)) + ;; Save the article. + (setq file (expand-file-name file)) + (unless (file-exists-p (file-name-directory file)) + (make-directory (file-name-directory file) t)) + (if (and message-fcc-handler-function + (not (eq message-fcc-handler-function 'rmail-output))) + (funcall message-fcc-handler-function file) + ;; FIXME this option, rmail-output (also used if + ;; message-fcc-handler-function is nil) is not + ;; documented anywhere AFAICS. It should work in Emacs + ;; 23; I suspect it does not work in Emacs 22. + ;; FIXME I don't see the need for the two different cases here. + ;; mail-use-rfc822 makes no difference (in Emacs 23),and + ;; the third argument just controls \"Wrote file\" message. + (if (and (file-readable-p file) (mail-file-babyl-p file)) + (rmail-output file 1 nil t) + (let ((mail-use-rfc822 t)) + (rmail-output file 1 t t)))))) + (kill-buffer (current-buffer)))))) + (defun notmuch-fcc-handler (fcc-header) "Store message with file fcc." (notmuch-maildir-fcc-file-fcc fcc-header)) diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index 500d6638..ad84c8a0 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -33,6 +33,7 @@ (declare-function notmuch-show-insert-body "notmuch-show" (msg body depth)) (declare-function notmuch-fcc-header-setup "notmuch-maildir-fcc" ()) (declare-function notmuch-fcc-handler "notmuch-maildir-fcc" (destdir)) +(declare-function notmuch-maildir-message-do-fcc "notmuch-maildir-fcc" ()) ;; @@ -490,12 +491,14 @@ will be addressed to all recipients of the source message." (defun notmuch-mua-send-and-exit (&optional arg) (interactive "P") (let ((message-fcc-handler-function #'notmuch-fcc-handler)) - (message-send-and-exit arg))) + (letf (((symbol-function 'message-do-fcc) #'notmuch-maildir-message-do-fcc)) + (message-send-and-exit arg)))) (defun notmuch-mua-send (&optional arg) (interactive "P") (let ((message-fcc-handler-function #'notmuch-fcc-handler)) - (message-send arg))) + (letf (((symbol-function 'message-do-fcc) #'notmuch-maildir-message-do-fcc)) + (message-send arg)))) (defun notmuch-mua-kill-buffer () (interactive) From aa1e8352de30a8dc272552ee65c7272166f39695 Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Sat, 3 Sep 2016 23:59:41 +0100 Subject: [PATCH 096/179] emacs: simplify our local copy of message-do-fcc message-do-fcc has lots of functionality we don't need, so remove it. --- emacs/notmuch-maildir-fcc.el | 31 +++---------------------------- emacs/notmuch-mua.el | 11 ++++------- 2 files changed, 7 insertions(+), 35 deletions(-) diff --git a/emacs/notmuch-maildir-fcc.el b/emacs/notmuch-maildir-fcc.el index 6fed11f2..b01f0098 100644 --- a/emacs/notmuch-maildir-fcc.el +++ b/emacs/notmuch-maildir-fcc.el @@ -127,7 +127,7 @@ by notmuch-mua-mail" (defun notmuch-maildir-message-do-fcc () "Process Fcc headers in the current buffer. -This is a direct copy from message-mode's message-do-fcc." +This is a rearranged version of message mode's message-do-fcc." (let ((case-fold-search t) (buf (current-buffer)) list file @@ -146,10 +146,7 @@ This is a direct copy from message-mode's message-do-fcc." (while (setq file (message-fetch-field "fcc" t)) (push file list) (message-remove-header "fcc" nil t)) - (let ((mail-parse-charset message-default-charset) - (rfc2047-header-encoding-alist - (cons '("Newsgroups" . default) - rfc2047-header-encoding-alist))) + (let ((mail-parse-charset message-default-charset)) (mail-encode-encoded-word-buffer))) (goto-char (point-min)) (when (re-search-forward @@ -159,29 +156,7 @@ This is a direct copy from message-mode's message-do-fcc." ;; Process FCC operations. (while list (setq file (pop list)) - (if (string-match "^[ \t]*|[ \t]*\\(.*\\)[ \t]*$" file) - ;; Pipe the article to the program in question. - (call-process-region (point-min) (point-max) shell-file-name - nil nil nil shell-command-switch - (match-string 1 file)) - ;; Save the article. - (setq file (expand-file-name file)) - (unless (file-exists-p (file-name-directory file)) - (make-directory (file-name-directory file) t)) - (if (and message-fcc-handler-function - (not (eq message-fcc-handler-function 'rmail-output))) - (funcall message-fcc-handler-function file) - ;; FIXME this option, rmail-output (also used if - ;; message-fcc-handler-function is nil) is not - ;; documented anywhere AFAICS. It should work in Emacs - ;; 23; I suspect it does not work in Emacs 22. - ;; FIXME I don't see the need for the two different cases here. - ;; mail-use-rfc822 makes no difference (in Emacs 23),and - ;; the third argument just controls \"Wrote file\" message. - (if (and (file-readable-p file) (mail-file-babyl-p file)) - (rmail-output file 1 nil t) - (let ((mail-use-rfc822 t)) - (rmail-output file 1 t t)))))) + (notmuch-fcc-handler file)) (kill-buffer (current-buffer)))))) (defun notmuch-fcc-handler (fcc-header) diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index ad84c8a0..fadf20fe 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -32,7 +32,6 @@ (declare-function notmuch-show-insert-body "notmuch-show" (msg body depth)) (declare-function notmuch-fcc-header-setup "notmuch-maildir-fcc" ()) -(declare-function notmuch-fcc-handler "notmuch-maildir-fcc" (destdir)) (declare-function notmuch-maildir-message-do-fcc "notmuch-maildir-fcc" ()) ;; @@ -490,15 +489,13 @@ will be addressed to all recipients of the source message." (defun notmuch-mua-send-and-exit (&optional arg) (interactive "P") - (let ((message-fcc-handler-function #'notmuch-fcc-handler)) - (letf (((symbol-function 'message-do-fcc) #'notmuch-maildir-message-do-fcc)) - (message-send-and-exit arg)))) + (letf (((symbol-function 'message-do-fcc) #'notmuch-maildir-message-do-fcc)) + (message-send-and-exit arg))) (defun notmuch-mua-send (&optional arg) (interactive "P") - (let ((message-fcc-handler-function #'notmuch-fcc-handler)) - (letf (((symbol-function 'message-do-fcc) #'notmuch-maildir-message-do-fcc)) - (message-send arg)))) + (letf (((symbol-function 'message-do-fcc) #'notmuch-maildir-message-do-fcc)) + (message-send arg))) (defun notmuch-mua-kill-buffer () (interactive) From cf59859b2028223cdf1741a2a09eadb666ccb726 Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Sat, 3 Sep 2016 23:59:42 +0100 Subject: [PATCH 097/179] Modify our local copy of message-do-fcc Since we also need to use this code for the draft handling we split message-do-fcc into convenient sub-chunks (functions or macros as appropriate). --- emacs/notmuch-maildir-fcc.el | 63 ++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 25 deletions(-) diff --git a/emacs/notmuch-maildir-fcc.el b/emacs/notmuch-maildir-fcc.el index b01f0098..cda51e7f 100644 --- a/emacs/notmuch-maildir-fcc.el +++ b/emacs/notmuch-maildir-fcc.el @@ -124,40 +124,53 @@ by notmuch-mua-mail" ;; Functions for saving a message either using notmuch insert or file ;; fcc. First functions common to the two cases. +(defmacro with-temporary-notmuch-message-buffer (&rest body) + "Set-up a temporary copy of the current message-mode buffer." + `(let ((case-fold-search t) + (buf (current-buffer)) + (mml-externalize-attachments message-fcc-externalize-attachments)) + (with-current-buffer (get-buffer-create " *message temp*") + (erase-buffer) + (insert-buffer-substring buf) + ,@body))) + +(defun notmuch-maildir-setup-message-for-saving () + "Setup message for saving. Should be called on a temporary copy. + +This is taken from the function message-do-fcc." + (message-encode-message-body) + (save-restriction + (message-narrow-to-headers) + (let ((mail-parse-charset message-default-charset)) + (mail-encode-encoded-word-buffer))) + (goto-char (point-min)) + (when (re-search-forward + (concat "^" (regexp-quote mail-header-separator) "$") + nil t) + (replace-match "" t t ))) + (defun notmuch-maildir-message-do-fcc () "Process Fcc headers in the current buffer. This is a rearranged version of message mode's message-do-fcc." - (let ((case-fold-search t) - (buf (current-buffer)) - list file - (mml-externalize-attachments message-fcc-externalize-attachments)) + (let (list file) (save-excursion (save-restriction (message-narrow-to-headers) (setq file (message-fetch-field "fcc" t))) (when file - (set-buffer (get-buffer-create " *message temp*")) - (erase-buffer) - (insert-buffer-substring buf) - (message-encode-message-body) - (save-restriction - (message-narrow-to-headers) - (while (setq file (message-fetch-field "fcc" t)) - (push file list) - (message-remove-header "fcc" nil t)) - (let ((mail-parse-charset message-default-charset)) - (mail-encode-encoded-word-buffer))) - (goto-char (point-min)) - (when (re-search-forward - (concat "^" (regexp-quote mail-header-separator) "$") - nil t) - (replace-match "" t t )) - ;; Process FCC operations. - (while list - (setq file (pop list)) - (notmuch-fcc-handler file)) - (kill-buffer (current-buffer)))))) + (with-temporary-notmuch-message-buffer + (save-restriction + (message-narrow-to-headers) + (while (setq file (message-fetch-field "fcc" t)) + (push file list) + (message-remove-header "fcc" nil t))) + (notmuch-maildir-setup-message-for-saving) + ;; Process FCC operations. + (while list + (setq file (pop list)) + (notmuch-fcc-handler file)) + (kill-buffer (current-buffer))))))) (defun notmuch-fcc-handler (fcc-header) "Store message with file fcc." From fce8146a8b738f2317034fee6236cdbd3597849b Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Sat, 3 Sep 2016 23:59:43 +0100 Subject: [PATCH 098/179] emacs: maildir: add the actual insert code With all the preparation it is now simple to add the actual insert code. Since insert can fail for many reasons we let the user decide interactively deal with it. We modify test-lib.el to set file fcc, so that all the old tests and emacs_fcc_message from test-lib.sh still work --- emacs/notmuch-maildir-fcc.el | 74 ++++++++++++++++++++++++++++++++++-- test/test-lib.el | 5 +++ 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/emacs/notmuch-maildir-fcc.el b/emacs/notmuch-maildir-fcc.el index cda51e7f..1218c01e 100644 --- a/emacs/notmuch-maildir-fcc.el +++ b/emacs/notmuch-maildir-fcc.el @@ -65,6 +65,12 @@ yet when sending a mail." :require 'notmuch-fcc-initialization :group 'notmuch-send) +(defcustom notmuch-maildir-use-notmuch-insert 't + "Should fcc use notmuch insert instead of simple fcc" + :type '(choice :tag "Fcc Method" + (const :tag "Use notmuch insert" t) + (const :tag "Use simple fcc" nil)) + :group 'notmuch-send) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Functions which set up the fcc header in the message buffer. @@ -108,7 +114,18 @@ by notmuch-mua-mail" (error "Invalid `notmuch-fcc-dirs' setting (neither string nor list)"))))) (when subdir - (notmuch-maildir-add-file-style-fcc-header subdir)))) + (if notmuch-maildir-use-notmuch-insert + (notmuch-maildir-add-notmuch-insert-style-fcc-header subdir) + (notmuch-maildir-add-file-style-fcc-header subdir))))) + +(defun notmuch-maildir-add-notmuch-insert-style-fcc-header (subdir) + ;; Notmuch insert does not accept absolute paths, so check the user + ;; really want this header inserted. + + (when (or (not (= (elt subdir 0) ?/)) + (y-or-n-p (format "Fcc header %s is an absolute path and notmuch insert is requested.\nInsert header anyway? " + subdir))) + (message-add-header (concat "Fcc: " subdir)))) (defun notmuch-maildir-add-file-style-fcc-header (subdir) (message-add-header @@ -173,8 +190,59 @@ This is a rearranged version of message mode's message-do-fcc." (kill-buffer (current-buffer))))))) (defun notmuch-fcc-handler (fcc-header) - "Store message with file fcc." - (notmuch-maildir-fcc-file-fcc fcc-header)) + "Store message with notmuch insert or normal (file) fcc. + +If `notmuch-maildir-use-notmuch-insert` is set then store the +message using notmuch insert. Otherwise store the message using +normal fcc." + (if notmuch-maildir-use-notmuch-insert + (notmuch-maildir-fcc-with-notmuch-insert fcc-header) + (notmuch-maildir-fcc-file-fcc fcc-header))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Functions for saving a message using notmuch insert. + +(defun notmuch-maildir-notmuch-insert-current-buffer (folder &optional create tags) + "Use notmuch insert to put the current buffer in the database. + +This inserts the current buffer as a message into the notmuch +database in folder FOLDER. If CREATE is non-nil it will supply +the --create-folder flag to create the folder if necessary. TAGS +should be a list of tag changes to apply to the inserted message." + (let* ((args (append (when create (list "--create-folder")) + (list (concat "--folder=" folder)) + tags))) + (apply 'notmuch-call-notmuch-process + :stdin-string (buffer-string) "insert" args))) + +(defun notmuch-maildir-fcc-with-notmuch-insert (fcc-header &optional create) + "Store message with notmuch insert. + +The fcc-header should be of the form \"folder +tag1 -tag2\" where +folder is the folder (relative to the notmuch mailstore) to store +the message in, and tag1 and tag2 are tag changes to apply to the +stored message. If CREATE is non-nil then create the folder if +necessary." + (let* ((args (split-string-and-unquote fcc-header)) + (folder (car args)) + (tags (cdr args))) + (condition-case nil + (notmuch-maildir-notmuch-insert-current-buffer folder create tags) + ;; Since there are many reasons notmuch insert could fail, e.g., + ;; locked database, non-existent folder (which could be due to a + ;; typo, or just the user want a new folder, let the user decide + ;; how to deal with it. + (error + (let ((response (read-char-choice + "Insert failed: (r)etry, (c)reate folder, (i)gnore, or (e)dit the header? " + '(?r ?c ?i ?e)))) + (case response + (?r (notmuch-maildir-fcc-with-notmuch-insert fcc-header)) + (?c (notmuch-maildir-fcc-with-notmuch-insert fcc-header 't)) + (?i 't) + (?e (notmuch-maildir-fcc-with-notmuch-insert + (read-from-minibuffer "Fcc header: " fcc-header))))))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Functions for saving a message using file fcc. diff --git a/test/test-lib.el b/test/test-lib.el index 496626f6..9946010b 100644 --- a/test/test-lib.el +++ b/test/test-lib.el @@ -184,6 +184,11 @@ nothing." (setq notmuch-tag-deleted-formats '((".*" nil))) +;; Also for historical reasons, we set the fcc handler to file not +;; insert. + +(setq notmuch-maildir-use-notmuch-insert nil) + ;; force a common html renderer, to avoid test variations between ;; environments From 7edded2740603eb9cde84f2c7b421026fbac3640 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Thu, 8 Sep 2016 09:03:15 -0300 Subject: [PATCH 099/179] test/crypto: hard code fingerprint Originally the intent was to make the test more robust against changing test keys. It turns out that (unscientifically) gpg --with-colons output changes more often than our test key. Rather than making the script more complex, just hard code the fingerprint. This fixes Debian bug #847013; I expect similar test failures as other distros adopt gnupg 2.1.15 --- test/T350-crypto.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/T350-crypto.sh b/test/T350-crypto.sh index 3656cce9..96349fa0 100755 --- a/test/T350-crypto.sh +++ b/test/T350-crypto.sh @@ -25,8 +25,8 @@ add_gnupg_home () ################################################## add_gnupg_home -# get key fingerprint -FINGERPRINT=$(gpg --no-tty --list-secret-keys --with-colons --fingerprint | grep '^fpr:' | cut -d: -f10) +# Change this if we ship a new test key +FINGERPRINT="5AEAB11F5E33DCE875DDB75B6D92612D94E46381" test_expect_success 'emacs delivery of signed message' \ 'emacs_fcc_message \ From afef67679378cde97153f262336880ffe14d7aed Mon Sep 17 00:00:00 2001 From: David Bremner Date: Thu, 8 Sep 2016 19:05:12 -0300 Subject: [PATCH 100/179] version: bump to 0.22.2 --- bindings/python/notmuch/version.py | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/python/notmuch/version.py b/bindings/python/notmuch/version.py index 0dcbb4e3..6d2b25fd 100644 --- a/bindings/python/notmuch/version.py +++ b/bindings/python/notmuch/version.py @@ -1,3 +1,3 @@ # this file should be kept in sync with ../../../version -__VERSION__ = '0.22.1' +__VERSION__ = '0.22.2' SOVERSION = '4' diff --git a/version b/version index a723ece7..faa5fb26 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.22.1 +0.22.2 From 79b3dda3f38ce95f6b3819df75c8ea6ea8c88994 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Thu, 8 Sep 2016 19:11:26 -0300 Subject: [PATCH 101/179] debian: add changelog stanza for 0.22.2-1 --- debian/changelog | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/debian/changelog b/debian/changelog index 1a7150d0..fb82015d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +notmuch (0.22.2-1) unstable; urgency=medium + + * Fix test suite compatibility with GnuPG 2.1.15. Bug fix: "FTBFS: + Tests failures", thanks to Lucas Nussbaum (Closes: #837013). + + -- David Bremner Thu, 08 Sep 2016 19:09:53 -0300 + notmuch (0.22.1-3) unstable; urgency=medium * Gag gdb even more. Bug fix: "FTBFS in testing", thanks to Santiago From da5029e2ac8dfd8a839de1f6a4701237d0b34db1 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Thu, 8 Sep 2016 19:22:12 -0300 Subject: [PATCH 102/179] NEWS: add news for 0.22.2 --- NEWS | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/NEWS b/NEWS index 3db6aba9..31d94274 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,18 @@ +Notmuch 0.22.2 (2016-09-08) +=========================== + +Test Suite +---------- + +Silence gdb more + + Have gdb write to a log file instead of stdout, hiding some more + (harmless) stderr chatter which causes test failures. + +Hardcode fingerprint in PGP/MIME tests + + Make the tests more robust against changing GnuPG output formats. + Notmuch 0.22.1 (2016-07-19) =========================== From e954310d70674a439a50cf4c699467567095b057 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Sun, 28 Aug 2016 14:41:22 -0400 Subject: [PATCH 103/179] emacs: use define-derived-mode for defining modes. This sets up and runs all the correct hooks and reduces some redundancy. --- emacs/notmuch-hello.el | 8 +------- emacs/notmuch-show.el | 7 +------ emacs/notmuch-tree.el | 7 +------ emacs/notmuch.el | 7 +------ 4 files changed, 4 insertions(+), 25 deletions(-) diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el index 75ccf579..d582bff7 100644 --- a/emacs/notmuch-hello.el +++ b/emacs/notmuch-hello.el @@ -671,7 +671,7 @@ with `notmuch-hello-query-counts'." "Keymap for \"notmuch hello\" buffers.") (fset 'notmuch-hello-mode-map notmuch-hello-mode-map) -(defun notmuch-hello-mode () +(define-derived-mode notmuch-hello-mode fundamental-mode "notmuch-hello" "Major mode for convenient notmuch navigation. This is your entry portal into notmuch. Saved searches are \"bookmarks\" for arbitrary queries. Hit RET @@ -702,13 +702,7 @@ The screen may be customized via `\\[customize]'. Complete list of currently available key bindings: \\{notmuch-hello-mode-map}" - (interactive) - (kill-all-local-variables) (setq notmuch-buffer-refresh-function #'notmuch-hello-update) - (use-local-map notmuch-hello-mode-map) - (setq major-mode 'notmuch-hello-mode - mode-name "notmuch-hello") - (run-mode-hooks 'notmuch-hello-mode-hook) ;;(setq buffer-read-only t) ) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index 6d3149bf..eb6877e5 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -1454,7 +1454,7 @@ reset based on the original query." "Keymap for \"notmuch show\" buffers.") (fset 'notmuch-show-mode-map notmuch-show-mode-map) -(defun notmuch-show-mode () +(define-derived-mode notmuch-show-mode fundamental-mode "notmuch-show" "Major mode for viewing a thread with notmuch. This buffer contains the results of the \"notmuch show\" command @@ -1482,12 +1482,7 @@ You can add or remove arbitrary tags from the current message with All currently available key bindings: \\{notmuch-show-mode-map}" - (interactive) - (kill-all-local-variables) (setq notmuch-buffer-refresh-function #'notmuch-show-refresh-view) - (use-local-map notmuch-show-mode-map) - (setq major-mode 'notmuch-show-mode - mode-name "notmuch-show") (setq buffer-read-only t truncate-lines t)) diff --git a/emacs/notmuch-tree.el b/emacs/notmuch-tree.el index 52313199..d864e6dd 100644 --- a/emacs/notmuch-tree.el +++ b/emacs/notmuch-tree.el @@ -803,7 +803,7 @@ This function inserts a collection of several complete threads as passed to it by notmuch-tree-process-filter." (mapc 'notmuch-tree-insert-forest-thread forest)) -(defun notmuch-tree-mode () +(define-derived-mode notmuch-tree-mode fundamental-mode "notmuch-tree" "Major mode displaying messages (as opposed to threads) of of a notmuch search. This buffer contains the results of a \"notmuch tree\" of your @@ -817,12 +817,7 @@ Complete list of currently available key bindings: \\{notmuch-tree-mode-map}" - (interactive) - (kill-all-local-variables) (setq notmuch-buffer-refresh-function #'notmuch-tree-refresh-view) - (use-local-map notmuch-tree-mode-map) - (setq major-mode 'notmuch-tree-mode - mode-name "notmuch-tree") (hl-line-mode 1) (setq buffer-read-only t truncate-lines t)) diff --git a/emacs/notmuch.el b/emacs/notmuch.el index 43d56f7b..ecf71980 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -331,7 +331,7 @@ This face is the default value for the \"unread\" tag in :group 'notmuch-search :group 'notmuch-faces) -(defun notmuch-search-mode () +(define-derived-mode notmuch-search-mode fundamental-mode "notmuch-search" "Major mode displaying results of a notmuch search. This buffer contains the results of a \"notmuch search\" of your @@ -361,8 +361,6 @@ new, global search. Complete list of currently available key bindings: \\{notmuch-search-mode-map}" - (interactive) - (kill-all-local-variables) (make-local-variable 'notmuch-search-query-string) (make-local-variable 'notmuch-search-oldest-first) (make-local-variable 'notmuch-search-target-thread) @@ -370,10 +368,7 @@ Complete list of currently available key bindings: (setq notmuch-buffer-refresh-function #'notmuch-search-refresh-view) (set (make-local-variable 'scroll-preserve-screen-position) t) (add-to-invisibility-spec (cons 'ellipsis t)) - (use-local-map notmuch-search-mode-map) (setq truncate-lines t) - (setq major-mode 'notmuch-search-mode - mode-name "notmuch-search") (setq buffer-read-only t)) (defun notmuch-search-get-result (&optional pos) From f94921520778ae4005500f5d1b943e2d4ddd3b2a Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Mon, 11 Jul 2016 08:49:19 +0100 Subject: [PATCH 104/179] emacs: show: let the user override the mime-type of an attachment This allows the user to override the mime-type of a part in the show buffer. This takes the simple option of displaying the part with the specified mime-type in its own buffer (in view mode). This avoids further complicating the part handling code. Bound to ". m" (i.e., m in the part map). Then the user can either enter a mime-type (with completion to all mime types that mailcap (and thus notmuch) knows about, or press return for the default choice of text/plain. --- emacs/notmuch-show.el | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index eb6877e5..5a585f38 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -1403,6 +1403,7 @@ reset based on the original query." (define-key map "v" 'notmuch-show-view-part) (define-key map "o" 'notmuch-show-interactively-view-part) (define-key map "|" 'notmuch-show-pipe-part) + (define-key map "m" 'notmuch-show-choose-mime-of-part) (define-key map "?" 'notmuch-subkeymap-help) map) "Submap for part commands") @@ -2332,25 +2333,27 @@ omit --in-reply-to=." (insert (notmuch-get-bodypart-binary msg part process-crypto))) buf)) -(defun notmuch-show-current-part-handle () +(defun notmuch-show-current-part-handle (&optional mime-type) "Return an mm-handle for the part containing point. This creates a temporary buffer for the part's content; the -caller is responsible for killing this buffer as appropriate." +caller is responsible for killing this buffer as appropriate. If +MIME-TYPE is given then set the handle's mime-type to MIME-TYPE." (let* ((msg (notmuch-show-get-message-properties)) (part (notmuch-show-get-part-properties)) (buf (notmuch-show-generate-part-buffer msg part)) - (computed-type (plist-get part :computed-type)) + (computed-type (or mime-type (plist-get part :computed-type))) (filename (plist-get part :filename)) (disposition (if filename `(attachment (filename . ,filename))))) (mm-make-handle buf (list computed-type) nil nil disposition))) -(defun notmuch-show-apply-to-current-part-handle (fn) +(defun notmuch-show-apply-to-current-part-handle (fn &optional mime-type) "Apply FN to an mm-handle for the part containing point. This ensures that the temporary buffer created for the mm-handle -is destroyed when FN returns." - (let ((handle (notmuch-show-current-part-handle))) +is destroyed when FN returns. If MIME-TYPE is given then force +part to be treated as if it had that mime-type." + (let ((handle (notmuch-show-current-part-handle mime-type))) ;; emacs 24.3+ puts stdout/stderr into the calling buffer so we ;; call it from a temp-buffer, unless ;; notmuch-show-attachment-debug is non-nil in which case we put @@ -2395,6 +2398,27 @@ is destroyed when FN returns." (notmuch-show-apply-to-current-part-handle #'mm-pipe-part)) +(defun notmuch-show--mm-display-part (handle) + "Use mm-display-part to display HANDLE in a new buffer. + +If the part is displayed in an external application then close +the new buffer." + (let ((buf (get-buffer-create (generate-new-buffer-name + (concat " *notmuch-internal-part*"))))) + (switch-to-buffer buf) + (if (eq (mm-display-part handle) 'external) + (kill-buffer buf) + (goto-char (point-min)) + (set-buffer-modified-p nil) + (view-buffer buf 'kill-buffer-if-not-modified)))) + +(defun notmuch-show-choose-mime-of-part (mime-type) + "Choose the mime type to use for displaying part" + (interactive + (list (completing-read "Mime type to use (default text/plain): " + (mailcap-mime-types) nil nil nil nil "text/plain"))) + (notmuch-show-apply-to-current-part-handle #'notmuch-show--mm-display-part mime-type)) + (provide 'notmuch-show) ;;; notmuch-show.el ends here From 7b7960ea228ca0449e2711377dfdebc23a791147 Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Sat, 6 Aug 2016 16:29:34 +0100 Subject: [PATCH 105/179] emacs: wash: make word-wrap bound message width Previously if notmuch-wash-wrap-lines-length was set then all messages would be wrapped at this value (or window-width if that is smaller). This was done regardless of the message's depth in a thread -- for example, if the n.w.w.l.l is 80 and the messages depth is 20 (so indented 20 by default) the messages text only got 60 characters of space. This commit changes that so a message always gets the full n.w.w.l.l of width regardless of its indentation (unless that goes over window-width of course). --- emacs/notmuch-wash.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/emacs/notmuch-wash.el b/emacs/notmuch-wash.el index 07fc1a1f..5f8b9267 100644 --- a/emacs/notmuch-wash.el +++ b/emacs/notmuch-wash.el @@ -122,8 +122,8 @@ collapse the remaining lines into a button." If this is nil, lines in messages will be wrapped to fit in the current window. If this is a number, lines will be wrapped after -this many characters or at the window width (whichever one is -lower)." +this many characters (ignoring indentation due to thread depth) +or at the window width (whichever one is lower)." :type '(choice (const :tag "window width" nil) (integer :tag "number of characters")) :group 'notmuch-wash) @@ -338,7 +338,7 @@ the wrapped text are maintained." (let* ((coolj-wrap-follows-window-size nil) (indent (* depth notmuch-show-indent-messages-width)) (limit (if (numberp notmuch-wash-wrap-lines-length) - (min notmuch-wash-wrap-lines-length + (min (+ notmuch-wash-wrap-lines-length indent) (window-width)) (window-width))) (fill-column (- limit From 3856286e21ab4ef282bbb262b700477282327923 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Sun, 14 Aug 2016 15:13:00 -0700 Subject: [PATCH 106/179] emacs: implement notmuch-search-color-line with dolist. While passing a lambda to mapc is idiomatic elisp, dolist is easier to understand, and there are a few other calls to it in this file. --- emacs/notmuch.el | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/emacs/notmuch.el b/emacs/notmuch.el index ecf71980..8e146924 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -695,13 +695,12 @@ background." (defun notmuch-search-color-line (start end line-tag-list) "Colorize lines in `notmuch-show' based on tags." - (mapc (lambda (elem) - (let ((tag (car elem)) - (attributes (cdr elem))) - (when (member tag line-tag-list) - (notmuch-apply-face nil attributes nil start end)))) - ;; Reverse the list so earlier entries take precedence - (reverse notmuch-search-line-faces))) + ;; Reverse the list so earlier entries take precedence + (dolist (elem (reverse notmuch-search-line-faces)) + (let ((tag (car elem)) + (face (cdr elem))) + (when (member tag line-tag-list) + (notmuch-apply-face nil face nil start end))))) (defun notmuch-search-author-propertize (authors) "Split `authors' into matching and non-matching authors and From d25d33ff2d818d5a423e07b7f465add9c970dc75 Mon Sep 17 00:00:00 2001 From: Matt Armstrong Date: Sun, 14 Aug 2016 15:17:42 -0700 Subject: [PATCH 107/179] emacs: make the remaining faces configurable. I believe this moves all "anonymous" face specifications in notmuch code into a configurable defface. --- NEWS | 8 ++++---- emacs/notmuch-tag.el | 45 +++++++++++++++++++++++++++++++++----------- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/NEWS b/NEWS index dbb92987..edbca4cd 100644 --- a/NEWS +++ b/NEWS @@ -6,10 +6,10 @@ Emacs Face customization is easier - New faces `notmuch-search-flagged-face` and - `notmuch-search-unread-face` are used by default by - `notmuch-search-line-faces`. Customize `notmuch-faces` to modify - them. + New faces `notmuch-tag-unread`, `notmuch-tag-flagged`, + `notmuch-tag-deleted`, `notmuch-tag-added`, + `notmuch-search-flagged-face` and `notmuch-search-unread-face` are + now used by default. Customize `notmuch-faces` to modify them. Ruby Bindings ------------- diff --git a/emacs/notmuch-tag.el b/emacs/notmuch-tag.el index a3f0c52a..ec3c964d 100644 --- a/emacs/notmuch-tag.el +++ b/emacs/notmuch-tag.el @@ -56,9 +56,23 @@ (string :tag "Custom"))) (sexp :tag "Custom"))))) +(defface notmuch-tag-unread + '((t :foreground "red")) + "Default face used for the unread tag. + +Used in the default value of `notmuch-tag-formats`." + :group 'notmuch-faces) + +(defface notmuch-tag-flagged + '((t :foreground "blue")) + "Face used for the flagged tag. + +Used in the default value of `notmuch-tag-formats`." + :group 'notmuch-faces) + (defcustom notmuch-tag-formats - '(("unread" (propertize tag 'face '(:foreground "red"))) - ("flagged" (propertize tag 'face '(:foreground "blue")) + '(("unread" (propertize tag 'face 'notmuch-tag-unread)) + ("flagged" (propertize tag 'face 'notmuch-tag-flagged) (notmuch-tag-format-image-data tag (notmuch-tag-star-icon)))) "Custom formats for individual tags. @@ -90,15 +104,17 @@ with images." :group 'notmuch-faces :type 'notmuch-tag-format-type) +(defface notmuch-tag-deleted + '((((class color) (supports :strike-through)) :strike-through "red") + (t :inverse-video t)) + "Face used to display deleted tags. + +Used in the default value of `notmuch-tag-deleted-formats`." + :group 'notmuch-faces) + (defcustom notmuch-tag-deleted-formats - '(("unread" (notmuch-apply-face bare-tag - (if (display-supports-face-attributes-p '(:strike-through "red")) - '(:strike-through "red") - '(:inverse-video t)))) - (".*" (notmuch-apply-face tag - (if (display-supports-face-attributes-p '(:strike-through "red")) - '(:strike-through "red") - '(:inverse-video t))))) + '(("unread" (notmuch-apply-face bare-tag `notmuch-tag-deleted)) + (".*" (notmuch-apply-face tag `notmuch-tag-deleted))) "Custom formats for tags when deleted. For deleted tags the formats in `notmuch-tag-formats` are applied @@ -118,8 +134,15 @@ See `notmuch-tag-formats' for full documentation." :group 'notmuch-faces :type 'notmuch-tag-format-type) +(defface notmuch-tag-added + '((t :underline "green")) + "Default face used for added tags. + +Used in the default value for `notmuch-tag-added-formats`." + :group 'notmuch-faces) + (defcustom notmuch-tag-added-formats - '((".*" (notmuch-apply-face tag '(:underline "green")))) + '((".*" (notmuch-apply-face tag 'notmuch-tag-added))) "Custom formats for tags when added. For added tags the formats in `notmuch-tag-formats` are applied From 89c8d279480aa618bed5ef074f9166dbf818e6eb Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Mon, 12 Sep 2016 04:23:48 +0200 Subject: [PATCH 108/179] clean up spelling --- NEWS | 4 ++-- doc/man1/notmuch-dump.rst | 2 +- doc/man1/notmuch-insert.rst | 4 ++-- doc/man1/notmuch-restore.rst | 2 +- doc/man5/notmuch-hooks.rst | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/NEWS b/NEWS index edbca4cd..c0b865df 100644 --- a/NEWS +++ b/NEWS @@ -483,13 +483,13 @@ Stopped `notmuch dump` failing if someone writes to the database process already has the write lock the dump will not start, so script callers should still check the return value. -`notmuch insert` requires succesful message indexing for success status +`notmuch insert` requires successful message indexing for success status Previously the `notmuch insert` subcommand indicated success even if the message indexing failed, as long as the message was delivered to file system. This could have lead to delivered messages missing tags, etc. `notmuch insert` is now more strict, also requiring - succesful indexing for success status. Use the new `--keep` option + successful indexing for success status. Use the new `--keep` option to revert to the old behaviour (keeping the delivered message file and returning success even if indexing fails). diff --git a/doc/man1/notmuch-dump.rst b/doc/man1/notmuch-dump.rst index 94986a86..d56974ed 100644 --- a/doc/man1/notmuch-dump.rst +++ b/doc/man1/notmuch-dump.rst @@ -78,7 +78,7 @@ Supported options for **dump** include **config** Output configuration data stored in the database. Each line - starts with "#@ ", followed by a space seperated key-value + starts with "#@ ", followed by a space separated key-value pair. Both key and value are hex encoded if needed. **tags** diff --git a/doc/man1/notmuch-insert.rst b/doc/man1/notmuch-insert.rst index 2c9c0d02..9e7afc36 100644 --- a/doc/man1/notmuch-insert.rst +++ b/doc/man1/notmuch-insert.rst @@ -45,7 +45,7 @@ Supported options for **insert** include Keep the message file if indexing fails, and keep the message indexed if applying tags or maildir flag synchronization fails. Ignore these errors and return exit status 0 to - indicate succesful mail delivery. + indicate successful mail delivery. ``--no-hooks`` Prevent hooks from being run. @@ -53,7 +53,7 @@ Supported options for **insert** include EXIT STATUS =========== -This command returns exit status 0 on succesful mail delivery, +This command returns exit status 0 on successful mail delivery, non-zero otherwise. The default is to indicate failed mail delivery on any errors, including message file delivery to the filesystem, message indexing to Notmuch database, changing tags, and synchronizing tags to diff --git a/doc/man1/notmuch-restore.rst b/doc/man1/notmuch-restore.rst index 87fa22ef..706f8912 100644 --- a/doc/man1/notmuch-restore.rst +++ b/doc/man1/notmuch-restore.rst @@ -57,7 +57,7 @@ Supported options for **restore** include **config** Restore configuration data to the database. Each configuration line starts - with "#@ ", followed by a space seperated key-value pair. + with "#@ ", followed by a space separated key-value pair. Both key and value are hex encoded if needed. **tags** diff --git a/doc/man5/notmuch-hooks.rst b/doc/man5/notmuch-hooks.rst index f96a923b..3a72a80b 100644 --- a/doc/man5/notmuch-hooks.rst +++ b/doc/man5/notmuch-hooks.rst @@ -41,7 +41,7 @@ The currently available hooks are described below. message has been delivered, added to the database, and initial tags have been applied. The hook will not be run if there have been any errors during the message delivery; what is regarded - as succesful delivery depends on the ``--keep`` option. + as successful delivery depends on the ``--keep`` option. Typically this hook is used to perform additional query-based tagging on the delivered messages. From 971cdc72cdb80f060193bc0914dc9badcc29696b Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 13 Sep 2016 20:14:08 +0300 Subject: [PATCH 109/179] test: make it possible to have multiple corpora We largely use the corpus under test/corpus for testing. Unfortunately, many of our tests have grown to depend on having exactly this set of messages, making it hard to add new message files for testing specific cases. We do use a lot of add_message from within the tests, but it's not possible to use that for adding broken messages, and adding several messages at once can get unwieldy. Move the basic corpus under tests/corpora/default, and make it possible to add new, independent corpora along its side. This means tons of renames with a few tweaks to add_email_corpus function in test-lib.sh to let tests specify which corpus to use. --- test/{corpus => corpora/default}/01:2, | 0 test/{corpus => corpora/default}/02:2, | 0 test/{corpus => corpora/default}/bar/17:2, | 0 test/{corpus => corpora/default}/bar/18:2, | 0 .../{corpus => corpora/default}/bar/baz/05:2, | 0 .../{corpus => corpora/default}/bar/baz/23:2, | 0 .../{corpus => corpora/default}/bar/baz/24:2, | 0 .../default}/bar/baz/cur/25:2, | 0 .../default}/bar/baz/cur/26:2, | 0 .../default}/bar/baz/new/27:2, | 0 .../default}/bar/baz/new/28:2, | 0 .../{corpus => corpora/default}/bar/cur/19:2, | 0 .../{corpus => corpora/default}/bar/cur/20:2, | 0 .../{corpus => corpora/default}/bar/new/21:2, | 0 .../{corpus => corpora/default}/bar/new/22:2, | 0 test/{corpus => corpora/default}/cur/29:2, | 0 test/{corpus => corpora/default}/cur/30:2, | 0 test/{corpus => corpora/default}/cur/31:2, | 0 test/{corpus => corpora/default}/cur/32:2, | 0 test/{corpus => corpora/default}/cur/33:2, | 0 test/{corpus => corpora/default}/cur/34:2, | 0 test/{corpus => corpora/default}/cur/35:2, | 0 test/{corpus => corpora/default}/cur/36:2, | 0 test/{corpus => corpora/default}/cur/37:2, | 0 test/{corpus => corpora/default}/cur/38:2, | 0 test/{corpus => corpora/default}/cur/39:2, | 0 test/{corpus => corpora/default}/cur/40:2, | 0 test/{corpus => corpora/default}/cur/41:2, | 0 test/{corpus => corpora/default}/cur/42:2, | 0 test/{corpus => corpora/default}/cur/43:2, | 0 test/{corpus => corpora/default}/cur/44:2, | 0 test/{corpus => corpora/default}/cur/45:2, | 0 test/{corpus => corpora/default}/cur/46:2, | 0 test/{corpus => corpora/default}/cur/47:2, | 0 test/{corpus => corpora/default}/cur/48:2, | 0 test/{corpus => corpora/default}/cur/49:2, | 0 test/{corpus => corpora/default}/cur/50:2, | 0 test/{corpus => corpora/default}/cur/51:2, | 0 test/{corpus => corpora/default}/cur/52:2, | 0 test/{corpus => corpora/default}/cur/53:2, | 0 test/{corpus => corpora/default}/foo/06:2, | 0 .../{corpus => corpora/default}/foo/baz/11:2, | 0 .../{corpus => corpora/default}/foo/baz/12:2, | 0 .../default}/foo/baz/cur/13:2, | 0 .../default}/foo/baz/cur/14:2, | 0 .../default}/foo/baz/new/15:2, | 0 .../default}/foo/baz/new/16:2, | 0 .../{corpus => corpora/default}/foo/cur/07:2, | 0 .../{corpus => corpora/default}/foo/cur/08:2, | 0 .../{corpus => corpora/default}/foo/new/03:2, | 0 .../{corpus => corpora/default}/foo/new/09:2, | 0 .../{corpus => corpora/default}/foo/new/10:2, | 0 test/{corpus => corpora/default}/new/04:2, | 0 test/test-lib.sh | 19 ++++++++++++------- 54 files changed, 12 insertions(+), 7 deletions(-) rename test/{corpus => corpora/default}/01:2, (100%) rename test/{corpus => corpora/default}/02:2, (100%) rename test/{corpus => corpora/default}/bar/17:2, (100%) rename test/{corpus => corpora/default}/bar/18:2, (100%) rename test/{corpus => corpora/default}/bar/baz/05:2, (100%) rename test/{corpus => corpora/default}/bar/baz/23:2, (100%) rename test/{corpus => corpora/default}/bar/baz/24:2, (100%) rename test/{corpus => corpora/default}/bar/baz/cur/25:2, (100%) rename test/{corpus => corpora/default}/bar/baz/cur/26:2, (100%) rename test/{corpus => corpora/default}/bar/baz/new/27:2, (100%) rename test/{corpus => corpora/default}/bar/baz/new/28:2, (100%) rename test/{corpus => corpora/default}/bar/cur/19:2, (100%) rename test/{corpus => corpora/default}/bar/cur/20:2, (100%) rename test/{corpus => corpora/default}/bar/new/21:2, (100%) rename test/{corpus => corpora/default}/bar/new/22:2, (100%) rename test/{corpus => corpora/default}/cur/29:2, (100%) rename test/{corpus => corpora/default}/cur/30:2, (100%) rename test/{corpus => corpora/default}/cur/31:2, (100%) rename test/{corpus => corpora/default}/cur/32:2, (100%) rename test/{corpus => corpora/default}/cur/33:2, (100%) rename test/{corpus => corpora/default}/cur/34:2, (100%) rename test/{corpus => corpora/default}/cur/35:2, (100%) rename test/{corpus => corpora/default}/cur/36:2, (100%) rename test/{corpus => corpora/default}/cur/37:2, (100%) rename test/{corpus => corpora/default}/cur/38:2, (100%) rename test/{corpus => corpora/default}/cur/39:2, (100%) rename test/{corpus => corpora/default}/cur/40:2, (100%) rename test/{corpus => corpora/default}/cur/41:2, (100%) rename test/{corpus => corpora/default}/cur/42:2, (100%) rename test/{corpus => corpora/default}/cur/43:2, (100%) rename test/{corpus => corpora/default}/cur/44:2, (100%) rename test/{corpus => corpora/default}/cur/45:2, (100%) rename test/{corpus => corpora/default}/cur/46:2, (100%) rename test/{corpus => corpora/default}/cur/47:2, (100%) rename test/{corpus => corpora/default}/cur/48:2, (100%) rename test/{corpus => corpora/default}/cur/49:2, (100%) rename test/{corpus => corpora/default}/cur/50:2, (100%) rename test/{corpus => corpora/default}/cur/51:2, (100%) rename test/{corpus => corpora/default}/cur/52:2, (100%) rename test/{corpus => corpora/default}/cur/53:2, (100%) rename test/{corpus => corpora/default}/foo/06:2, (100%) rename test/{corpus => corpora/default}/foo/baz/11:2, (100%) rename test/{corpus => corpora/default}/foo/baz/12:2, (100%) rename test/{corpus => corpora/default}/foo/baz/cur/13:2, (100%) rename test/{corpus => corpora/default}/foo/baz/cur/14:2, (100%) rename test/{corpus => corpora/default}/foo/baz/new/15:2, (100%) rename test/{corpus => corpora/default}/foo/baz/new/16:2, (100%) rename test/{corpus => corpora/default}/foo/cur/07:2, (100%) rename test/{corpus => corpora/default}/foo/cur/08:2, (100%) rename test/{corpus => corpora/default}/foo/new/03:2, (100%) rename test/{corpus => corpora/default}/foo/new/09:2, (100%) rename test/{corpus => corpora/default}/foo/new/10:2, (100%) rename test/{corpus => corpora/default}/new/04:2, (100%) diff --git a/test/corpus/01:2, b/test/corpora/default/01:2, similarity index 100% rename from test/corpus/01:2, rename to test/corpora/default/01:2, diff --git a/test/corpus/02:2, b/test/corpora/default/02:2, similarity index 100% rename from test/corpus/02:2, rename to test/corpora/default/02:2, diff --git a/test/corpus/bar/17:2, b/test/corpora/default/bar/17:2, similarity index 100% rename from test/corpus/bar/17:2, rename to test/corpora/default/bar/17:2, diff --git a/test/corpus/bar/18:2, b/test/corpora/default/bar/18:2, similarity index 100% rename from test/corpus/bar/18:2, rename to test/corpora/default/bar/18:2, diff --git a/test/corpus/bar/baz/05:2, b/test/corpora/default/bar/baz/05:2, similarity index 100% rename from test/corpus/bar/baz/05:2, rename to test/corpora/default/bar/baz/05:2, diff --git a/test/corpus/bar/baz/23:2, b/test/corpora/default/bar/baz/23:2, similarity index 100% rename from test/corpus/bar/baz/23:2, rename to test/corpora/default/bar/baz/23:2, diff --git a/test/corpus/bar/baz/24:2, b/test/corpora/default/bar/baz/24:2, similarity index 100% rename from test/corpus/bar/baz/24:2, rename to test/corpora/default/bar/baz/24:2, diff --git a/test/corpus/bar/baz/cur/25:2, b/test/corpora/default/bar/baz/cur/25:2, similarity index 100% rename from test/corpus/bar/baz/cur/25:2, rename to test/corpora/default/bar/baz/cur/25:2, diff --git a/test/corpus/bar/baz/cur/26:2, b/test/corpora/default/bar/baz/cur/26:2, similarity index 100% rename from test/corpus/bar/baz/cur/26:2, rename to test/corpora/default/bar/baz/cur/26:2, diff --git a/test/corpus/bar/baz/new/27:2, b/test/corpora/default/bar/baz/new/27:2, similarity index 100% rename from test/corpus/bar/baz/new/27:2, rename to test/corpora/default/bar/baz/new/27:2, diff --git a/test/corpus/bar/baz/new/28:2, b/test/corpora/default/bar/baz/new/28:2, similarity index 100% rename from test/corpus/bar/baz/new/28:2, rename to test/corpora/default/bar/baz/new/28:2, diff --git a/test/corpus/bar/cur/19:2, b/test/corpora/default/bar/cur/19:2, similarity index 100% rename from test/corpus/bar/cur/19:2, rename to test/corpora/default/bar/cur/19:2, diff --git a/test/corpus/bar/cur/20:2, b/test/corpora/default/bar/cur/20:2, similarity index 100% rename from test/corpus/bar/cur/20:2, rename to test/corpora/default/bar/cur/20:2, diff --git a/test/corpus/bar/new/21:2, b/test/corpora/default/bar/new/21:2, similarity index 100% rename from test/corpus/bar/new/21:2, rename to test/corpora/default/bar/new/21:2, diff --git a/test/corpus/bar/new/22:2, b/test/corpora/default/bar/new/22:2, similarity index 100% rename from test/corpus/bar/new/22:2, rename to test/corpora/default/bar/new/22:2, diff --git a/test/corpus/cur/29:2, b/test/corpora/default/cur/29:2, similarity index 100% rename from test/corpus/cur/29:2, rename to test/corpora/default/cur/29:2, diff --git a/test/corpus/cur/30:2, b/test/corpora/default/cur/30:2, similarity index 100% rename from test/corpus/cur/30:2, rename to test/corpora/default/cur/30:2, diff --git a/test/corpus/cur/31:2, b/test/corpora/default/cur/31:2, similarity index 100% rename from test/corpus/cur/31:2, rename to test/corpora/default/cur/31:2, diff --git a/test/corpus/cur/32:2, b/test/corpora/default/cur/32:2, similarity index 100% rename from test/corpus/cur/32:2, rename to test/corpora/default/cur/32:2, diff --git a/test/corpus/cur/33:2, b/test/corpora/default/cur/33:2, similarity index 100% rename from test/corpus/cur/33:2, rename to test/corpora/default/cur/33:2, diff --git a/test/corpus/cur/34:2, b/test/corpora/default/cur/34:2, similarity index 100% rename from test/corpus/cur/34:2, rename to test/corpora/default/cur/34:2, diff --git a/test/corpus/cur/35:2, b/test/corpora/default/cur/35:2, similarity index 100% rename from test/corpus/cur/35:2, rename to test/corpora/default/cur/35:2, diff --git a/test/corpus/cur/36:2, b/test/corpora/default/cur/36:2, similarity index 100% rename from test/corpus/cur/36:2, rename to test/corpora/default/cur/36:2, diff --git a/test/corpus/cur/37:2, b/test/corpora/default/cur/37:2, similarity index 100% rename from test/corpus/cur/37:2, rename to test/corpora/default/cur/37:2, diff --git a/test/corpus/cur/38:2, b/test/corpora/default/cur/38:2, similarity index 100% rename from test/corpus/cur/38:2, rename to test/corpora/default/cur/38:2, diff --git a/test/corpus/cur/39:2, b/test/corpora/default/cur/39:2, similarity index 100% rename from test/corpus/cur/39:2, rename to test/corpora/default/cur/39:2, diff --git a/test/corpus/cur/40:2, b/test/corpora/default/cur/40:2, similarity index 100% rename from test/corpus/cur/40:2, rename to test/corpora/default/cur/40:2, diff --git a/test/corpus/cur/41:2, b/test/corpora/default/cur/41:2, similarity index 100% rename from test/corpus/cur/41:2, rename to test/corpora/default/cur/41:2, diff --git a/test/corpus/cur/42:2, b/test/corpora/default/cur/42:2, similarity index 100% rename from test/corpus/cur/42:2, rename to test/corpora/default/cur/42:2, diff --git a/test/corpus/cur/43:2, b/test/corpora/default/cur/43:2, similarity index 100% rename from test/corpus/cur/43:2, rename to test/corpora/default/cur/43:2, diff --git a/test/corpus/cur/44:2, b/test/corpora/default/cur/44:2, similarity index 100% rename from test/corpus/cur/44:2, rename to test/corpora/default/cur/44:2, diff --git a/test/corpus/cur/45:2, b/test/corpora/default/cur/45:2, similarity index 100% rename from test/corpus/cur/45:2, rename to test/corpora/default/cur/45:2, diff --git a/test/corpus/cur/46:2, b/test/corpora/default/cur/46:2, similarity index 100% rename from test/corpus/cur/46:2, rename to test/corpora/default/cur/46:2, diff --git a/test/corpus/cur/47:2, b/test/corpora/default/cur/47:2, similarity index 100% rename from test/corpus/cur/47:2, rename to test/corpora/default/cur/47:2, diff --git a/test/corpus/cur/48:2, b/test/corpora/default/cur/48:2, similarity index 100% rename from test/corpus/cur/48:2, rename to test/corpora/default/cur/48:2, diff --git a/test/corpus/cur/49:2, b/test/corpora/default/cur/49:2, similarity index 100% rename from test/corpus/cur/49:2, rename to test/corpora/default/cur/49:2, diff --git a/test/corpus/cur/50:2, b/test/corpora/default/cur/50:2, similarity index 100% rename from test/corpus/cur/50:2, rename to test/corpora/default/cur/50:2, diff --git a/test/corpus/cur/51:2, b/test/corpora/default/cur/51:2, similarity index 100% rename from test/corpus/cur/51:2, rename to test/corpora/default/cur/51:2, diff --git a/test/corpus/cur/52:2, b/test/corpora/default/cur/52:2, similarity index 100% rename from test/corpus/cur/52:2, rename to test/corpora/default/cur/52:2, diff --git a/test/corpus/cur/53:2, b/test/corpora/default/cur/53:2, similarity index 100% rename from test/corpus/cur/53:2, rename to test/corpora/default/cur/53:2, diff --git a/test/corpus/foo/06:2, b/test/corpora/default/foo/06:2, similarity index 100% rename from test/corpus/foo/06:2, rename to test/corpora/default/foo/06:2, diff --git a/test/corpus/foo/baz/11:2, b/test/corpora/default/foo/baz/11:2, similarity index 100% rename from test/corpus/foo/baz/11:2, rename to test/corpora/default/foo/baz/11:2, diff --git a/test/corpus/foo/baz/12:2, b/test/corpora/default/foo/baz/12:2, similarity index 100% rename from test/corpus/foo/baz/12:2, rename to test/corpora/default/foo/baz/12:2, diff --git a/test/corpus/foo/baz/cur/13:2, b/test/corpora/default/foo/baz/cur/13:2, similarity index 100% rename from test/corpus/foo/baz/cur/13:2, rename to test/corpora/default/foo/baz/cur/13:2, diff --git a/test/corpus/foo/baz/cur/14:2, b/test/corpora/default/foo/baz/cur/14:2, similarity index 100% rename from test/corpus/foo/baz/cur/14:2, rename to test/corpora/default/foo/baz/cur/14:2, diff --git a/test/corpus/foo/baz/new/15:2, b/test/corpora/default/foo/baz/new/15:2, similarity index 100% rename from test/corpus/foo/baz/new/15:2, rename to test/corpora/default/foo/baz/new/15:2, diff --git a/test/corpus/foo/baz/new/16:2, b/test/corpora/default/foo/baz/new/16:2, similarity index 100% rename from test/corpus/foo/baz/new/16:2, rename to test/corpora/default/foo/baz/new/16:2, diff --git a/test/corpus/foo/cur/07:2, b/test/corpora/default/foo/cur/07:2, similarity index 100% rename from test/corpus/foo/cur/07:2, rename to test/corpora/default/foo/cur/07:2, diff --git a/test/corpus/foo/cur/08:2, b/test/corpora/default/foo/cur/08:2, similarity index 100% rename from test/corpus/foo/cur/08:2, rename to test/corpora/default/foo/cur/08:2, diff --git a/test/corpus/foo/new/03:2, b/test/corpora/default/foo/new/03:2, similarity index 100% rename from test/corpus/foo/new/03:2, rename to test/corpora/default/foo/new/03:2, diff --git a/test/corpus/foo/new/09:2, b/test/corpora/default/foo/new/09:2, similarity index 100% rename from test/corpus/foo/new/09:2, rename to test/corpora/default/foo/new/09:2, diff --git a/test/corpus/foo/new/10:2, b/test/corpora/default/foo/new/10:2, similarity index 100% rename from test/corpus/foo/new/10:2, rename to test/corpora/default/foo/new/10:2, diff --git a/test/corpus/new/04:2, b/test/corpora/default/new/04:2, similarity index 100% rename from test/corpus/new/04:2, rename to test/corpora/default/new/04:2, diff --git a/test/test-lib.sh b/test/test-lib.sh index aac0343b..e2e26e6f 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -541,21 +541,26 @@ emacs_fcc_message () notmuch new >/dev/null } -# Generate a corpus of email and add it to the database. +# Add an existing, fixed corpus of email to the database. # -# This corpus is fixed, (it happens to be 50 messages from early in -# the history of the notmuch mailing list), which allows for reliably +# $1 is the corpus dir under corpora to add, using "default" if unset. +# +# The default corpus is based on about 50 messages from early in the +# history of the notmuch mailing list, which allows for reliably # testing commands that need to operate on a not-totally-trivial # number of messages. add_email_corpus () { + corpus=${1:-default} + rm -rf ${MAIL_DIR} - if [ -d $TEST_DIRECTORY/corpus.mail ]; then - cp -a $TEST_DIRECTORY/corpus.mail ${MAIL_DIR} + if [ -d $TEST_DIRECTORY/corpora.mail/$corpus ]; then + cp -a $TEST_DIRECTORY/corpora.mail/$corpus ${MAIL_DIR} else - cp -a $TEST_DIRECTORY/corpus ${MAIL_DIR} + cp -a $TEST_DIRECTORY/corpora/$corpus ${MAIL_DIR} notmuch new >/dev/null || die "'notmuch new' failed while adding email corpus" - cp -a ${MAIL_DIR} $TEST_DIRECTORY/corpus.mail + mkdir -p $TEST_DIRECTORY/corpora.mail + cp -a ${MAIL_DIR} $TEST_DIRECTORY/corpora.mail/$corpus fi } From 36416c74e09d07fe3dfff79900171f15c4671412 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 13 Sep 2016 20:14:09 +0300 Subject: [PATCH 110/179] test: add known broken test for reply to message with multiple Cc headers As Daniel Kahn Gillmor reports in id:87d1ngv95p.fsf@alice.fifthhorseman.net, notmuch show combines multiple Cc: fields into one, while notmuch reply does not. While such messages are in violation of RFC 5322, it would be reasonable to expect notmuch to be consistent. Add a known broken test to document this expectation. This also starts a new "broken" corpus for messages which are broken. Details: The original message is formatted using the message printing in notmuch-show.c. For Cc:, it uses g_mime_message_get_recipients(), which apparently combines all Cc: fields into one internally. The addresses in the reply headers, OTOH, are based on headers queried through libnotmuch. It boils down to g_mime_object_get_header() in lib/message-file.c, which returns only the first occurence of header. --- test/T220-reply.sh | 13 +++++++++++++ test/corpora/README | 11 +++++++++++ test/corpora/broken/broken-cc | 9 +++++++++ 3 files changed, 33 insertions(+) create mode 100644 test/corpora/README create mode 100644 test/corpora/broken/broken-cc diff --git a/test/T220-reply.sh b/test/T220-reply.sh index 30b78f67..a72068dd 100755 --- a/test/T220-reply.sh +++ b/test/T220-reply.sh @@ -253,5 +253,18 @@ test_expect_equal_json "$output" ' } }' +test_begin_subtest "Reply to a message with multiple Cc headers" +test_subtest_known_broken +add_email_corpus broken +output=$(notmuch reply id:multiple-cc@example.org) +test_expect_equal "$output" "From: Notmuch Test Suite +Subject: Re: wowsers! +To: Alice , Daniel +Cc: Bob , Charles +In-Reply-To: +References: + +On Thu, 16 Jun 2016 22:14:41 -0400, Alice wrote: +> Note the Cc: and cc: headers." test_done diff --git a/test/corpora/README b/test/corpora/README new file mode 100644 index 00000000..77c48e6e --- /dev/null +++ b/test/corpora/README @@ -0,0 +1,11 @@ +This directory contains email corpora for testing. + +default + The default corpus is based on about 50 messages from early in the + history of the notmuch mailing list, which allows for reliably + testing commands that need to operate on a not-totally-trivial + number of messages. + +broken + The broken corpus contains messages that are broken and/or RFC + non-compliant, ensuring we deal with them in a sane way. diff --git a/test/corpora/broken/broken-cc b/test/corpora/broken/broken-cc new file mode 100644 index 00000000..57ae9ba9 --- /dev/null +++ b/test/corpora/broken/broken-cc @@ -0,0 +1,9 @@ +From: Alice +To: Daniel +Cc: Bob +Subject: wowsers! +cc: Charles +Message-Id: +Date: Thu, 16 Jun 2016 22:14:41 -0400 + +Note the Cc: and cc: headers. From 0c5840862e2f8802b2d280dbf77da908d3617b10 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 13 Sep 2016 20:14:10 +0300 Subject: [PATCH 111/179] cli/reply: push notmuch reply format abstraction lower in the stack There's quite a bit of duplication, and some consequent deviation, between the various notmuch reply format code paths. Perform the query and message iteration in common code, and make the format specific functions operate on single messages. There should be no functional changes. --- notmuch-reply.c | 224 +++++++++++++++++++++++------------------------- 1 file changed, 105 insertions(+), 119 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index 49513732..847e306f 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -598,82 +598,42 @@ create_reply_message(void *ctx, static int notmuch_reply_format_default(void *ctx, notmuch_config_t *config, - notmuch_query_t *query, + notmuch_message_t *message, notmuch_show_params_t *params, notmuch_bool_t reply_all, unused (sprinter_t *sp)) { GMimeMessage *reply; - notmuch_messages_t *messages; - notmuch_message_t *message; mime_node_t *root; - notmuch_status_t status; - status = notmuch_query_search_messages_st (query, &messages); - if (print_status_query ("notmuch reply", query, status)) + reply = create_reply_message (ctx, config, message, reply_all); + if (!reply) return 1; - for (; - notmuch_messages_valid (messages); - notmuch_messages_move_to_next (messages)) - { - message = notmuch_messages_get (messages); + show_reply_headers (reply); - reply = create_reply_message (ctx, config, message, reply_all); + g_object_unref (G_OBJECT (reply)); - /* If reply creation failed, we're out of memory, so don't - * bother trying any more messages. - */ - if (!reply) { - notmuch_message_destroy (message); - return 1; - } - - show_reply_headers (reply); - - g_object_unref (G_OBJECT (reply)); - reply = NULL; - - if (mime_node_open (ctx, message, &(params->crypto), &root) == NOTMUCH_STATUS_SUCCESS) { - format_part_reply (root); - talloc_free (root); - } - - notmuch_message_destroy (message); + if (mime_node_open (ctx, message, ¶ms->crypto, &root) == NOTMUCH_STATUS_SUCCESS) { + format_part_reply (root); + talloc_free (root); } + return 0; } static int notmuch_reply_format_sprinter(void *ctx, notmuch_config_t *config, - notmuch_query_t *query, + notmuch_message_t *message, notmuch_show_params_t *params, notmuch_bool_t reply_all, sprinter_t *sp) { GMimeMessage *reply; - notmuch_messages_t *messages; - notmuch_message_t *message; mime_node_t *node; - unsigned count; - notmuch_status_t status; - status = notmuch_query_count_messages_st (query, &count); - if (print_status_query ("notmuch reply", query, status)) - return 1; - - if (count != 1) { - fprintf (stderr, "Error: search term did not match precisely one message (matched %d messages).\n", count); - return 1; - } - - status = notmuch_query_search_messages_st (query, &messages); - if (print_status_query ("notmuch reply", query, status)) - return 1; - - message = notmuch_messages_get (messages); - if (mime_node_open (ctx, message, &(params->crypto), &node) != NOTMUCH_STATUS_SUCCESS) + if (mime_node_open (ctx, message, ¶ms->crypto, &node) != NOTMUCH_STATUS_SUCCESS) return 1; reply = create_reply_message (ctx, config, message, reply_all); @@ -686,7 +646,6 @@ notmuch_reply_format_sprinter(void *ctx, sp->map_key (sp, "reply-headers"); format_headers_sprinter (sp, reply, TRUE); g_object_unref (G_OBJECT (reply)); - reply = NULL; /* Start the original */ sp->map_key (sp, "original"); @@ -694,7 +653,6 @@ notmuch_reply_format_sprinter(void *ctx, /* End */ sp->end (sp); - notmuch_message_destroy (message); return 0; } @@ -703,17 +661,100 @@ notmuch_reply_format_sprinter(void *ctx, static int notmuch_reply_format_headers_only(void *ctx, notmuch_config_t *config, - notmuch_query_t *query, + notmuch_message_t *message, unused (notmuch_show_params_t *params), notmuch_bool_t reply_all, unused (sprinter_t *sp)) { GMimeMessage *reply; - notmuch_messages_t *messages; - notmuch_message_t *message; const char *in_reply_to, *orig_references, *references; char *reply_headers; + + /* The 0 means we do not want headers in a "pretty" order. */ + reply = g_mime_message_new (0); + if (reply == NULL) { + fprintf (stderr, "Out of memory\n"); + return 1; + } + + in_reply_to = talloc_asprintf (ctx, "<%s>", + notmuch_message_get_message_id (message)); + + g_mime_object_set_header (GMIME_OBJECT (reply), "In-Reply-To", in_reply_to); + + orig_references = notmuch_message_get_header (message, "references"); + + /* + * We print In-Reply-To followed by References because git + * format-patch treats them specially. Git does not interpret the + * other headers specially. + */ + references = talloc_asprintf (ctx, "%s%s%s", + orig_references ? orig_references : "", + orig_references ? " " : "", + in_reply_to); + g_mime_object_set_header (GMIME_OBJECT (reply), "References", references); + + (void)add_recipients_from_message (reply, config, message, reply_all); + + reply_headers = g_mime_object_to_string (GMIME_OBJECT (reply)); + printf ("%s", reply_headers); + free (reply_headers); + + g_object_unref (G_OBJECT (reply)); + + return 0; +} + +enum { + FORMAT_DEFAULT, + FORMAT_JSON, + FORMAT_SEXP, + FORMAT_HEADERS_ONLY, +}; + +static int do_reply(notmuch_config_t *config, + notmuch_query_t *query, + notmuch_show_params_t *params, + int format, + notmuch_bool_t reply_all) +{ + notmuch_messages_t *messages; + notmuch_message_t *message; notmuch_status_t status; + struct sprinter *sp = NULL; + int ret = 0; + int (*reply_format_func) (void *ctx, + notmuch_config_t *config, + notmuch_message_t *message, + notmuch_show_params_t *params, + notmuch_bool_t reply_all, + struct sprinter *sp); + + if (format == FORMAT_JSON || format == FORMAT_SEXP) { + unsigned count; + + status = notmuch_query_count_messages_st (query, &count); + if (print_status_query ("notmuch reply", query, status)) + return 1; + + if (count != 1) { + fprintf (stderr, "Error: search term did not match precisely one message (matched %d messages).\n", count); + return 1; + } + } + + if (format == FORMAT_HEADERS_ONLY) { + reply_format_func = notmuch_reply_format_headers_only; + } else if (format == FORMAT_JSON) { + reply_format_func = notmuch_reply_format_sprinter; + sp = sprinter_json_create (config, stdout); + } else if (format == FORMAT_SEXP) { + reply_format_func = notmuch_reply_format_sprinter; + sp = sprinter_sexp_create (config, stdout); + } else { + reply_format_func = notmuch_reply_format_default; + } status = notmuch_query_search_messages_st (query, &messages); if (print_status_query ("notmuch reply", query, status)) @@ -725,52 +766,16 @@ notmuch_reply_format_headers_only(void *ctx, { message = notmuch_messages_get (messages); - /* The 0 means we do not want headers in a "pretty" order. */ - reply = g_mime_message_new (0); - if (reply == NULL) { - fprintf (stderr, "Out of memory\n"); - return 1; - } - - in_reply_to = talloc_asprintf (ctx, "<%s>", - notmuch_message_get_message_id (message)); - - g_mime_object_set_header (GMIME_OBJECT (reply), - "In-Reply-To", in_reply_to); - - - orig_references = notmuch_message_get_header (message, "references"); - - /* We print In-Reply-To followed by References because git format-patch treats them - * specially. Git does not interpret the other headers specially - */ - references = talloc_asprintf (ctx, "%s%s%s", - orig_references ? orig_references : "", - orig_references ? " " : "", - in_reply_to); - g_mime_object_set_header (GMIME_OBJECT (reply), - "References", references); - - (void)add_recipients_from_message (reply, config, message, reply_all); - - reply_headers = g_mime_object_to_string (GMIME_OBJECT (reply)); - printf ("%s", reply_headers); - free (reply_headers); - - g_object_unref (G_OBJECT (reply)); - reply = NULL; + ret = reply_format_func(config, config, message, params, reply_all, sp); notmuch_message_destroy (message); - } - return 0; -} -enum { - FORMAT_DEFAULT, - FORMAT_JSON, - FORMAT_SEXP, - FORMAT_HEADERS_ONLY, -}; + if (ret) + break; + } + + return ret; +} int notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[]) @@ -779,12 +784,6 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_query_t *query; char *query_string; int opt_index; - int (*reply_format_func) (void *ctx, - notmuch_config_t *config, - notmuch_query_t *query, - notmuch_show_params_t *params, - notmuch_bool_t reply_all, - struct sprinter *sp); notmuch_show_params_t params = { .part = -1, .crypto = { @@ -795,7 +794,6 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[]) }; int format = FORMAT_DEFAULT; int reply_all = TRUE; - struct sprinter *sp = NULL; notmuch_opt_desc_t options[] = { { NOTMUCH_OPT_KEYWORD, &format, "format", 'f', @@ -820,18 +818,6 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_process_shared_options (argv[0]); - if (format == FORMAT_HEADERS_ONLY) { - reply_format_func = notmuch_reply_format_headers_only; - } else if (format == FORMAT_JSON) { - reply_format_func = notmuch_reply_format_sprinter; - sp = sprinter_json_create (config, stdout); - } else if (format == FORMAT_SEXP) { - reply_format_func = notmuch_reply_format_sprinter; - sp = sprinter_sexp_create (config, stdout); - } else { - reply_format_func = notmuch_reply_format_default; - } - notmuch_exit_if_unsupported_format (); query_string = query_string_from_args (config, argc-opt_index, argv+opt_index); @@ -859,7 +845,7 @@ notmuch_reply_command (notmuch_config_t *config, int argc, char *argv[]) return EXIT_FAILURE; } - if (reply_format_func (config, config, query, ¶ms, reply_all, sp) != 0) + if (do_reply (config, query, ¶ms, format, reply_all) != 0) return EXIT_FAILURE; notmuch_crypto_cleanup (¶ms.crypto); From a843fa48fedb7a3ce02af412404ba4359cdce5e0 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 13 Sep 2016 20:14:11 +0300 Subject: [PATCH 112/179] cli/reply: reuse show_reply_headers() in headers-only format Align the code with default format reply. There should be no changes in output. --- notmuch-reply.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index 847e306f..5adbab62 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -668,7 +668,6 @@ notmuch_reply_format_headers_only(void *ctx, { GMimeMessage *reply; const char *in_reply_to, *orig_references, *references; - char *reply_headers; /* The 0 means we do not want headers in a "pretty" order. */ reply = g_mime_message_new (0); @@ -697,9 +696,7 @@ notmuch_reply_format_headers_only(void *ctx, (void)add_recipients_from_message (reply, config, message, reply_all); - reply_headers = g_mime_object_to_string (GMIME_OBJECT (reply)); - printf ("%s", reply_headers); - free (reply_headers); + show_reply_headers (reply); g_object_unref (G_OBJECT (reply)); From 208053b6845010aac08e00be50063ee890066584 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 13 Sep 2016 20:14:12 +0300 Subject: [PATCH 113/179] cli/reply: unify reply format functions Prepare for further future unification by making the code similar. The only functional change is that errors in mime_node_open() also break execution in default reply format. --- notmuch-reply.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index 5adbab62..4b97ffa4 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -604,20 +604,20 @@ notmuch_reply_format_default(void *ctx, unused (sprinter_t *sp)) { GMimeMessage *reply; - mime_node_t *root; + mime_node_t *node; + + if (mime_node_open (ctx, message, ¶ms->crypto, &node)) + return 1; reply = create_reply_message (ctx, config, message, reply_all); if (!reply) return 1; show_reply_headers (reply); + format_part_reply (node); g_object_unref (G_OBJECT (reply)); - - if (mime_node_open (ctx, message, ¶ms->crypto, &root) == NOTMUCH_STATUS_SUCCESS) { - format_part_reply (root); - talloc_free (root); - } + talloc_free (node); return 0; } @@ -633,7 +633,7 @@ notmuch_reply_format_sprinter(void *ctx, GMimeMessage *reply; mime_node_t *node; - if (mime_node_open (ctx, message, ¶ms->crypto, &node) != NOTMUCH_STATUS_SUCCESS) + if (mime_node_open (ctx, message, ¶ms->crypto, &node)) return 1; reply = create_reply_message (ctx, config, message, reply_all); @@ -645,7 +645,6 @@ notmuch_reply_format_sprinter(void *ctx, /* The headers of the reply message we've created */ sp->map_key (sp, "reply-headers"); format_headers_sprinter (sp, reply, TRUE); - g_object_unref (G_OBJECT (reply)); /* Start the original */ sp->map_key (sp, "original"); @@ -654,6 +653,9 @@ notmuch_reply_format_sprinter(void *ctx, /* End */ sp->end (sp); + g_object_unref (G_OBJECT (reply)); + talloc_free (node); + return 0; } From b1aca0e502e97ab822da60aa4217d4b0bdb33543 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 13 Sep 2016 20:14:13 +0300 Subject: [PATCH 114/179] cli/reply: reorganize create_reply_message() Again, in preparation for later unification, reorganize create_reply_message() to be more similar to the headers-only format reply code in notmuch_reply_format_headers_only(). Due to "pretty" header ordering, there should be no change in output. There should be no functional changes. --- notmuch-reply.c | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index 4b97ffa4..eb074055 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -532,12 +532,20 @@ create_reply_message(void *ctx, return NULL; } - subject = notmuch_message_get_header (message, "subject"); - if (subject) { - if (strncasecmp (subject, "Re:", 3)) - subject = talloc_asprintf (ctx, "Re: %s", subject); - g_mime_message_set_subject (reply, subject); - } + in_reply_to = talloc_asprintf (ctx, "<%s>", + notmuch_message_get_message_id (message)); + + g_mime_object_set_header (GMIME_OBJECT (reply), "In-Reply-To", in_reply_to); + + orig_references = notmuch_message_get_header (message, "references"); + if (!orig_references) + /* Treat errors like missing References headers. */ + orig_references = ""; + references = talloc_asprintf (ctx, "%s%s%s", + *orig_references ? orig_references : "", + *orig_references ? " " : "", + in_reply_to); + g_mime_object_set_header (GMIME_OBJECT (reply), "References", references); from_addr = add_recipients_from_message (reply, config, message, reply_all); @@ -572,25 +580,14 @@ create_reply_message(void *ctx, from_addr = talloc_asprintf (ctx, "%s <%s>", notmuch_config_get_user_name (config), from_addr); - g_mime_object_set_header (GMIME_OBJECT (reply), - "From", from_addr); + g_mime_object_set_header (GMIME_OBJECT (reply), "From", from_addr); - in_reply_to = talloc_asprintf (ctx, "<%s>", - notmuch_message_get_message_id (message)); - - g_mime_object_set_header (GMIME_OBJECT (reply), - "In-Reply-To", in_reply_to); - - orig_references = notmuch_message_get_header (message, "references"); - if (!orig_references) - /* Treat errors like missing References headers. */ - orig_references = ""; - references = talloc_asprintf (ctx, "%s%s%s", - *orig_references ? orig_references : "", - *orig_references ? " " : "", - in_reply_to); - g_mime_object_set_header (GMIME_OBJECT (reply), - "References", references); + subject = notmuch_message_get_header (message, "subject"); + if (subject) { + if (strncasecmp (subject, "Re:", 3)) + subject = talloc_asprintf (ctx, "Re: %s", subject); + g_mime_message_set_subject (reply, subject); + } return reply; } From 1e289ed1c9fb8af8696c2735b714438bd115725f Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 13 Sep 2016 20:14:14 +0300 Subject: [PATCH 115/179] cli/reply: make references header creation easier to follow Just use strdup when original references is not available, instead of trying to cram everything into a monster asprintf. There should be no functional changes. --- notmuch-reply.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index eb074055..c2d7402d 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -538,13 +538,12 @@ create_reply_message(void *ctx, g_mime_object_set_header (GMIME_OBJECT (reply), "In-Reply-To", in_reply_to); orig_references = notmuch_message_get_header (message, "references"); - if (!orig_references) - /* Treat errors like missing References headers. */ - orig_references = ""; - references = talloc_asprintf (ctx, "%s%s%s", - *orig_references ? orig_references : "", - *orig_references ? " " : "", - in_reply_to); + if (orig_references && *orig_references) + references = talloc_asprintf (ctx, "%s %s", orig_references, + in_reply_to); + else + references = talloc_strdup (ctx, in_reply_to); + g_mime_object_set_header (GMIME_OBJECT (reply), "References", references); from_addr = add_recipients_from_message (reply, config, From 5e438d95c4671844cf4fddbd16c5744f41cda85c Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 13 Sep 2016 20:14:15 +0300 Subject: [PATCH 116/179] cli/reply: reuse create_reply_message() also for headers-only format Add an option for "limited" headers for the (slightly misleadingly named) headers-only format. There should be no functional changes. --- notmuch-reply.c | 46 +++++++++++++++------------------------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index c2d7402d..daad453e 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -520,13 +520,17 @@ static GMimeMessage * create_reply_message(void *ctx, notmuch_config_t *config, notmuch_message_t *message, - notmuch_bool_t reply_all) + notmuch_bool_t reply_all, + notmuch_bool_t limited) { const char *subject, *from_addr = NULL; const char *in_reply_to, *orig_references, *references; - /* The 1 means we want headers in a "pretty" order. */ - GMimeMessage *reply = g_mime_message_new (1); + /* + * Use the below header order for limited headers, "pretty" order + * otherwise. + */ + GMimeMessage *reply = g_mime_message_new (limited ? 0 : 1); if (reply == NULL) { fprintf (stderr, "Out of memory\n"); return NULL; @@ -549,6 +553,10 @@ create_reply_message(void *ctx, from_addr = add_recipients_from_message (reply, config, message, reply_all); + /* The above is all that is needed for limited headers. */ + if (limited) + return reply; + /* * Sadly, there is no standard way to find out to which email * address a mail was delivered - what is in the headers depends @@ -605,7 +613,7 @@ notmuch_reply_format_default(void *ctx, if (mime_node_open (ctx, message, ¶ms->crypto, &node)) return 1; - reply = create_reply_message (ctx, config, message, reply_all); + reply = create_reply_message (ctx, config, message, reply_all, FALSE); if (!reply) return 1; @@ -632,7 +640,7 @@ notmuch_reply_format_sprinter(void *ctx, if (mime_node_open (ctx, message, ¶ms->crypto, &node)) return 1; - reply = create_reply_message (ctx, config, message, reply_all); + reply = create_reply_message (ctx, config, message, reply_all, FALSE); if (!reply) return 1; @@ -665,34 +673,10 @@ notmuch_reply_format_headers_only(void *ctx, unused (sprinter_t *sp)) { GMimeMessage *reply; - const char *in_reply_to, *orig_references, *references; - /* The 0 means we do not want headers in a "pretty" order. */ - reply = g_mime_message_new (0); - if (reply == NULL) { - fprintf (stderr, "Out of memory\n"); + reply = create_reply_message (ctx, config, message, reply_all, TRUE); + if (!reply) return 1; - } - - in_reply_to = talloc_asprintf (ctx, "<%s>", - notmuch_message_get_message_id (message)); - - g_mime_object_set_header (GMIME_OBJECT (reply), "In-Reply-To", in_reply_to); - - orig_references = notmuch_message_get_header (message, "references"); - - /* - * We print In-Reply-To followed by References because git - * format-patch treats them specially. Git does not interpret the - * other headers specially. - */ - references = talloc_asprintf (ctx, "%s%s%s", - orig_references ? orig_references : "", - orig_references ? " " : "", - in_reply_to); - g_mime_object_set_header (GMIME_OBJECT (reply), "References", references); - - (void)add_recipients_from_message (reply, config, message, reply_all); show_reply_headers (reply); From 301a65b0f2f7e4551ded7104d779b8600a806b2b Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 13 Sep 2016 20:14:16 +0300 Subject: [PATCH 117/179] cli/reply: reduce the reply format abstractions Now that we've made the various reply formats quite similar to each other, there's no point in keeping the abstractions. They are now close enough to be put in one function. For now, a mime node will be uselessly created for the headers-only case, but this is insignificant, and may change in the future. --- notmuch-reply.c | 145 ++++++++++++------------------------------------ 1 file changed, 36 insertions(+), 109 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index daad453e..b380678e 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -599,92 +599,6 @@ create_reply_message(void *ctx, return reply; } -static int -notmuch_reply_format_default(void *ctx, - notmuch_config_t *config, - notmuch_message_t *message, - notmuch_show_params_t *params, - notmuch_bool_t reply_all, - unused (sprinter_t *sp)) -{ - GMimeMessage *reply; - mime_node_t *node; - - if (mime_node_open (ctx, message, ¶ms->crypto, &node)) - return 1; - - reply = create_reply_message (ctx, config, message, reply_all, FALSE); - if (!reply) - return 1; - - show_reply_headers (reply); - format_part_reply (node); - - g_object_unref (G_OBJECT (reply)); - talloc_free (node); - - return 0; -} - -static int -notmuch_reply_format_sprinter(void *ctx, - notmuch_config_t *config, - notmuch_message_t *message, - notmuch_show_params_t *params, - notmuch_bool_t reply_all, - sprinter_t *sp) -{ - GMimeMessage *reply; - mime_node_t *node; - - if (mime_node_open (ctx, message, ¶ms->crypto, &node)) - return 1; - - reply = create_reply_message (ctx, config, message, reply_all, FALSE); - if (!reply) - return 1; - - sp->begin_map (sp); - - /* The headers of the reply message we've created */ - sp->map_key (sp, "reply-headers"); - format_headers_sprinter (sp, reply, TRUE); - - /* Start the original */ - sp->map_key (sp, "original"); - format_part_sprinter (ctx, sp, node, TRUE, TRUE, FALSE); - - /* End */ - sp->end (sp); - - g_object_unref (G_OBJECT (reply)); - talloc_free (node); - - return 0; -} - -/* This format is currently tuned for a git send-email --notmuch hook */ -static int -notmuch_reply_format_headers_only(void *ctx, - notmuch_config_t *config, - notmuch_message_t *message, - unused (notmuch_show_params_t *params), - notmuch_bool_t reply_all, - unused (sprinter_t *sp)) -{ - GMimeMessage *reply; - - reply = create_reply_message (ctx, config, message, reply_all, TRUE); - if (!reply) - return 1; - - show_reply_headers (reply); - - g_object_unref (G_OBJECT (reply)); - - return 0; -} - enum { FORMAT_DEFAULT, FORMAT_JSON, @@ -698,17 +612,12 @@ static int do_reply(notmuch_config_t *config, int format, notmuch_bool_t reply_all) { + GMimeMessage *reply; + mime_node_t *node; notmuch_messages_t *messages; notmuch_message_t *message; notmuch_status_t status; struct sprinter *sp = NULL; - int ret = 0; - int (*reply_format_func) (void *ctx, - notmuch_config_t *config, - notmuch_message_t *message, - notmuch_show_params_t *params, - notmuch_bool_t reply_all, - struct sprinter *sp); if (format == FORMAT_JSON || format == FORMAT_SEXP) { unsigned count; @@ -721,18 +630,11 @@ static int do_reply(notmuch_config_t *config, fprintf (stderr, "Error: search term did not match precisely one message (matched %d messages).\n", count); return 1; } - } - if (format == FORMAT_HEADERS_ONLY) { - reply_format_func = notmuch_reply_format_headers_only; - } else if (format == FORMAT_JSON) { - reply_format_func = notmuch_reply_format_sprinter; - sp = sprinter_json_create (config, stdout); - } else if (format == FORMAT_SEXP) { - reply_format_func = notmuch_reply_format_sprinter; - sp = sprinter_sexp_create (config, stdout); - } else { - reply_format_func = notmuch_reply_format_default; + if (format == FORMAT_JSON) + sp = sprinter_json_create (config, stdout); + else + sp = sprinter_sexp_create (config, stdout); } status = notmuch_query_search_messages_st (query, &messages); @@ -745,15 +647,40 @@ static int do_reply(notmuch_config_t *config, { message = notmuch_messages_get (messages); - ret = reply_format_func(config, config, message, params, reply_all, sp); + if (mime_node_open (config, message, ¶ms->crypto, &node)) + return 1; + + reply = create_reply_message (config, config, message, reply_all, + format == FORMAT_HEADERS_ONLY); + if (!reply) + return 1; + + if (format == FORMAT_JSON || format == FORMAT_SEXP) { + sp->begin_map (sp); + + /* The headers of the reply message we've created */ + sp->map_key (sp, "reply-headers"); + format_headers_sprinter (sp, reply, TRUE); + + /* Start the original */ + sp->map_key (sp, "original"); + format_part_sprinter (config, sp, node, TRUE, TRUE, FALSE); + + /* End */ + sp->end (sp); + } else { + show_reply_headers (reply); + if (format == FORMAT_DEFAULT) + format_part_reply (node); + } + + g_object_unref (G_OBJECT (reply)); + talloc_free (node); notmuch_message_destroy (message); - - if (ret) - break; } - return ret; + return 0; } int From 78520673fa9e42c3f0856913a00a9bf3c7a05489 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 13 Sep 2016 20:14:17 +0300 Subject: [PATCH 118/179] cli/reply: use dedicated functions for reply to mapping The main motivation here is to move the special casing around reply-to/from handling into a function of its own, clarifying the main logic. --- notmuch-reply.c | 82 +++++++++++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 33 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index b380678e..9b78ea2c 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -256,17 +256,13 @@ scan_address_string (const char *recipients, * in either the 'To' or 'Cc' header of the message? */ static int -reply_to_header_is_redundant (notmuch_message_t *message) +reply_to_header_is_redundant (notmuch_message_t *message, const char *reply_to) { - const char *reply_to, *to, *cc, *addr; + const char *to, *cc, *addr; InternetAddressList *list; InternetAddress *address; InternetAddressMailbox *mailbox; - reply_to = notmuch_message_get_header (message, "reply-to"); - if (reply_to == NULL || *reply_to == '\0') - return 0; - list = internet_address_list_parse_string (reply_to); if (internet_address_list_length (list) != 1) @@ -291,6 +287,47 @@ reply_to_header_is_redundant (notmuch_message_t *message) return 0; } +static const char *get_sender(notmuch_message_t *message) +{ + const char *reply_to; + + reply_to = notmuch_message_get_header (message, "reply-to"); + if (reply_to && *reply_to) { + /* + * Some mailing lists munge the Reply-To header despite it + * being A Bad Thing, see + * http://marc.merlins.org/netrants/reply-to-harmful.html + * + * The munging is easy to detect, because it results in a + * redundant reply-to header, (with an address that already + * exists in either To or Cc). So in this case, we ignore the + * Reply-To field and use the From header. This ensures the + * original sender will get the reply even if not subscribed + * to the list. Note that the address in the Reply-To header + * will always appear in the reply if reply_all is true. + */ + if (! reply_to_header_is_redundant (message, reply_to)) + return reply_to; + } + + return notmuch_message_get_header (message, "from"); +} + +static const char *get_to(notmuch_message_t *message) +{ + return notmuch_message_get_header (message, "to"); +} + +static const char *get_cc(notmuch_message_t *message) +{ + return notmuch_message_get_header (message, "cc"); +} + +static const char *get_bcc(notmuch_message_t *message) +{ + return notmuch_message_get_header (message, "bcc"); +} + /* Augment the recipients of 'reply' from the "Reply-to:", "From:", * "To:", "Cc:", and "Bcc:" headers of 'message'. * @@ -310,43 +347,22 @@ add_recipients_from_message (GMimeMessage *reply, notmuch_bool_t reply_all) { struct { - const char *header; - const char *fallback; + const char * (*get_header)(notmuch_message_t *message); GMimeRecipientType recipient_type; } reply_to_map[] = { - { "reply-to", "from", GMIME_RECIPIENT_TYPE_TO }, - { "to", NULL, GMIME_RECIPIENT_TYPE_TO }, - { "cc", NULL, GMIME_RECIPIENT_TYPE_CC }, - { "bcc", NULL, GMIME_RECIPIENT_TYPE_BCC } + { get_sender, GMIME_RECIPIENT_TYPE_TO }, + { get_to, GMIME_RECIPIENT_TYPE_TO }, + { get_cc, GMIME_RECIPIENT_TYPE_CC }, + { get_bcc, GMIME_RECIPIENT_TYPE_BCC }, }; const char *from_addr = NULL; unsigned int i; unsigned int n = 0; - /* Some mailing lists munge the Reply-To header despite it being A Bad - * Thing, see http://marc.merlins.org/netrants/reply-to-harmful.html - * - * The munging is easy to detect, because it results in a - * redundant reply-to header, (with an address that already exists - * in either To or Cc). So in this case, we ignore the Reply-To - * field and use the From header. This ensures the original sender - * will get the reply even if not subscribed to the list. Note - * that the address in the Reply-To header will always appear in - * the reply if reply_all is true. - */ - if (reply_to_header_is_redundant (message)) { - reply_to_map[0].header = "from"; - reply_to_map[0].fallback = NULL; - } - for (i = 0; i < ARRAY_SIZE (reply_to_map); i++) { const char *recipients; - recipients = notmuch_message_get_header (message, - reply_to_map[i].header); - if ((recipients == NULL || recipients[0] == '\0') && reply_to_map[i].fallback) - recipients = notmuch_message_get_header (message, - reply_to_map[i].fallback); + recipients = reply_to_map[i].get_header (message); n += scan_address_string (recipients, config, reply, reply_to_map[i].recipient_type, &from_addr); From ca82d481a1040ed3a47ad9c5e7cf7ec16e5e2ced Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 13 Sep 2016 20:14:18 +0300 Subject: [PATCH 119/179] cli/reply: check for NULL list first in scan_address_list() Support passing NULL list later on. Also use it to simplify the recursion. --- notmuch-reply.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index 9b78ea2c..d90f46f9 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -192,6 +192,9 @@ scan_address_list (InternetAddressList *list, int i; unsigned int n = 0; + if (list == NULL) + return 0; + for (i = 0; i < internet_address_list_length (list); i++) { address = internet_address_list_get_address (list, i); if (INTERNET_ADDRESS_IS_GROUP (address)) { @@ -200,9 +203,6 @@ scan_address_list (InternetAddressList *list, group = INTERNET_ADDRESS_GROUP (address); group_list = internet_address_group_get_members (group); - if (group_list == NULL) - continue; - n += scan_address_list (group_list, config, message, type, user_from); } else { InternetAddressMailbox *mailbox; From 536b1f9df989915161024d2334e206f538d59f9b Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 13 Sep 2016 20:14:19 +0300 Subject: [PATCH 120/179] cli/reply: return internet address list from get header funcs Pass in GMimeMessage to simplify To/Cc/Bcc headers. We'll eventually remove the notmuch message passing altogether, but keep both for now to not make too big changes at once. Getting the headers from GMimeMessage using GMime functions fixes the error on duplicate Cc headers reported by Daniel Kahn Gillmor in id:87d1ngv95p.fsf@alice.fifthhorseman.net. Get rid of an intermediate function. The small annoyance is the ownership differences in the address lists. --- notmuch-reply.c | 73 +++++++++++++++++++--------------------------- test/T220-reply.sh | 1 - 2 files changed, 30 insertions(+), 44 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index d90f46f9..98034485 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -227,31 +227,6 @@ scan_address_list (InternetAddressList *list, return n; } -/* Scan addresses in 'recipients'. - * - * See the documentation of scan_address_list() above. This function - * does exactly the same, but converts 'recipients' to an - * InternetAddressList first. - */ -static unsigned int -scan_address_string (const char *recipients, - notmuch_config_t *config, - GMimeMessage *message, - GMimeRecipientType type, - const char **user_from) -{ - InternetAddressList *list; - - if (recipients == NULL) - return 0; - - list = internet_address_list_parse_string (recipients); - if (list == NULL) - return 0; - - return scan_address_list (list, config, message, type, user_from); -} - /* Does the address in the Reply-To header of 'message' already appear * in either the 'To' or 'Cc' header of the message? */ @@ -287,11 +262,12 @@ reply_to_header_is_redundant (notmuch_message_t *message, const char *reply_to) return 0; } -static const char *get_sender(notmuch_message_t *message) +static InternetAddressList *get_sender(notmuch_message_t *message, + GMimeMessage *mime_message) { const char *reply_to; - reply_to = notmuch_message_get_header (message, "reply-to"); + reply_to = g_mime_message_get_reply_to (mime_message); if (reply_to && *reply_to) { /* * Some mailing lists munge the Reply-To header despite it @@ -307,25 +283,32 @@ static const char *get_sender(notmuch_message_t *message) * will always appear in the reply if reply_all is true. */ if (! reply_to_header_is_redundant (message, reply_to)) - return reply_to; + return internet_address_list_parse_string (reply_to); } - return notmuch_message_get_header (message, "from"); + return internet_address_list_parse_string ( + g_mime_message_get_sender (mime_message)); } -static const char *get_to(notmuch_message_t *message) +static InternetAddressList *get_to(unused(notmuch_message_t *message), + GMimeMessage *mime_message) { - return notmuch_message_get_header (message, "to"); + return g_mime_message_get_recipients (mime_message, + GMIME_RECIPIENT_TYPE_TO); } -static const char *get_cc(notmuch_message_t *message) +static InternetAddressList *get_cc(unused(notmuch_message_t *message), + GMimeMessage *mime_message) { - return notmuch_message_get_header (message, "cc"); + return g_mime_message_get_recipients (mime_message, + GMIME_RECIPIENT_TYPE_CC); } -static const char *get_bcc(notmuch_message_t *message) +static InternetAddressList *get_bcc(unused(notmuch_message_t *message), + GMimeMessage *mime_message) { - return notmuch_message_get_header (message, "bcc"); + return g_mime_message_get_recipients (mime_message, + GMIME_RECIPIENT_TYPE_BCC); } /* Augment the recipients of 'reply' from the "Reply-to:", "From:", @@ -344,10 +327,12 @@ static const char * add_recipients_from_message (GMimeMessage *reply, notmuch_config_t *config, notmuch_message_t *message, + GMimeMessage *mime_message, notmuch_bool_t reply_all) { struct { - const char * (*get_header)(notmuch_message_t *message); + InternetAddressList * (*get_header)(notmuch_message_t *message, + GMimeMessage *mime_message); GMimeRecipientType recipient_type; } reply_to_map[] = { { get_sender, GMIME_RECIPIENT_TYPE_TO }, @@ -360,12 +345,12 @@ add_recipients_from_message (GMimeMessage *reply, unsigned int n = 0; for (i = 0; i < ARRAY_SIZE (reply_to_map); i++) { - const char *recipients; + InternetAddressList *recipients; - recipients = reply_to_map[i].get_header (message); + recipients = reply_to_map[i].get_header (message, mime_message); - n += scan_address_string (recipients, config, reply, - reply_to_map[i].recipient_type, &from_addr); + n += scan_address_list (recipients, config, reply, + reply_to_map[i].recipient_type, &from_addr); if (!reply_all && n) { /* Stop adding new recipients in reply-to-sender mode if @@ -536,6 +521,7 @@ static GMimeMessage * create_reply_message(void *ctx, notmuch_config_t *config, notmuch_message_t *message, + GMimeMessage *mime_message, notmuch_bool_t reply_all, notmuch_bool_t limited) { @@ -566,8 +552,8 @@ create_reply_message(void *ctx, g_mime_object_set_header (GMIME_OBJECT (reply), "References", references); - from_addr = add_recipients_from_message (reply, config, - message, reply_all); + from_addr = add_recipients_from_message (reply, config, message, + mime_message, reply_all); /* The above is all that is needed for limited headers. */ if (limited) @@ -666,7 +652,8 @@ static int do_reply(notmuch_config_t *config, if (mime_node_open (config, message, ¶ms->crypto, &node)) return 1; - reply = create_reply_message (config, config, message, reply_all, + reply = create_reply_message (config, config, message, + GMIME_MESSAGE (node->part), reply_all, format == FORMAT_HEADERS_ONLY); if (!reply) return 1; diff --git a/test/T220-reply.sh b/test/T220-reply.sh index a72068dd..818a8654 100755 --- a/test/T220-reply.sh +++ b/test/T220-reply.sh @@ -254,7 +254,6 @@ test_expect_equal_json "$output" ' }' test_begin_subtest "Reply to a message with multiple Cc headers" -test_subtest_known_broken add_email_corpus broken output=$(notmuch reply id:multiple-cc@example.org) test_expect_equal "$output" "From: Notmuch Test Suite From 932c0ff879ddaf38f1e4ae325c061132d6af1026 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 13 Sep 2016 20:14:20 +0300 Subject: [PATCH 121/179] cli/reply: do not parse Reply-To: header into internet address list twice Avoid parsing Reply-To: header into internet address list twice. Move the parsing outside of reply_to_header_is_redundant(), and pass the parsed internet address list in as parameter. This also avoids leaking the memory of one copy of the internet address list. --- notmuch-reply.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index 98034485..cf4248bd 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -231,19 +231,18 @@ scan_address_list (InternetAddressList *list, * in either the 'To' or 'Cc' header of the message? */ static int -reply_to_header_is_redundant (notmuch_message_t *message, const char *reply_to) +reply_to_header_is_redundant (notmuch_message_t *message, + InternetAddressList *reply_to_list) { const char *to, *cc, *addr; - InternetAddressList *list; InternetAddress *address; InternetAddressMailbox *mailbox; - list = internet_address_list_parse_string (reply_to); - - if (internet_address_list_length (list) != 1) + if (reply_to_list == NULL || + internet_address_list_length (reply_to_list) != 1) return 0; - address = internet_address_list_get_address (list, 0); + address = internet_address_list_get_address (reply_to_list, 0); if (INTERNET_ADDRESS_IS_GROUP (address)) return 0; @@ -269,6 +268,8 @@ static InternetAddressList *get_sender(notmuch_message_t *message, reply_to = g_mime_message_get_reply_to (mime_message); if (reply_to && *reply_to) { + InternetAddressList *reply_to_list; + /* * Some mailing lists munge the Reply-To header despite it * being A Bad Thing, see @@ -282,8 +283,11 @@ static InternetAddressList *get_sender(notmuch_message_t *message, * to the list. Note that the address in the Reply-To header * will always appear in the reply if reply_all is true. */ - if (! reply_to_header_is_redundant (message, reply_to)) - return internet_address_list_parse_string (reply_to); + reply_to_list = internet_address_list_parse_string (reply_to); + if (! reply_to_header_is_redundant (message, reply_to_list)) + return reply_to_list; + + g_object_unref (G_OBJECT (reply_to_list)); } return internet_address_list_parse_string ( From e20a7a2fbcaa21926b3ae8c199839b792433767a Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 13 Sep 2016 20:14:21 +0300 Subject: [PATCH 122/179] cli/reply: pass gmime message to Reply-To: redundancy detection Use gmime message instead of notmuch message in Reply-To: redundancy detection. This allows us to easily iterate over all recipient email addresses accurately, instead of just scanning for strings in the relevant message headers. This improves the accuracy of the detection in many ways. This also makes the notmuch message parameter to get_sender() unused. This will be cleaned up in a follow-up patch to not make too many changes here at once. --- notmuch-reply.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index cf4248bd..5421ca80 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -230,13 +230,16 @@ scan_address_list (InternetAddressList *list, /* Does the address in the Reply-To header of 'message' already appear * in either the 'To' or 'Cc' header of the message? */ -static int -reply_to_header_is_redundant (notmuch_message_t *message, +static notmuch_bool_t +reply_to_header_is_redundant (GMimeMessage *message, InternetAddressList *reply_to_list) { - const char *to, *cc, *addr; + const char *addr, *reply_to; InternetAddress *address; InternetAddressMailbox *mailbox; + InternetAddressList *recipients; + notmuch_bool_t ret = FALSE; + int i; if (reply_to_list == NULL || internet_address_list_length (reply_to_list) != 1) @@ -247,21 +250,29 @@ reply_to_header_is_redundant (notmuch_message_t *message, return 0; mailbox = INTERNET_ADDRESS_MAILBOX (address); - addr = internet_address_mailbox_get_addr (mailbox); + reply_to = internet_address_mailbox_get_addr (mailbox); - to = notmuch_message_get_header (message, "to"); - cc = notmuch_message_get_header (message, "cc"); + recipients = g_mime_message_get_all_recipients (message); - if ((to && strstr (to, addr) != 0) || - (cc && strstr (cc, addr) != 0)) - { - return 1; + for (i = 0; i < internet_address_list_length (recipients); i++) { + address = internet_address_list_get_address (recipients, i); + if (INTERNET_ADDRESS_IS_GROUP (address)) + continue; + + mailbox = INTERNET_ADDRESS_MAILBOX (address); + addr = internet_address_mailbox_get_addr (mailbox); + if (strcmp (addr, reply_to) == 0) { + ret = TRUE; + break; + } } - return 0; + g_object_unref (G_OBJECT (recipients)); + + return ret; } -static InternetAddressList *get_sender(notmuch_message_t *message, +static InternetAddressList *get_sender(unused(notmuch_message_t *message), GMimeMessage *mime_message) { const char *reply_to; @@ -284,7 +295,7 @@ static InternetAddressList *get_sender(notmuch_message_t *message, * will always appear in the reply if reply_all is true. */ reply_to_list = internet_address_list_parse_string (reply_to); - if (! reply_to_header_is_redundant (message, reply_to_list)) + if (! reply_to_header_is_redundant (mime_message, reply_to_list)) return reply_to_list; g_object_unref (G_OBJECT (reply_to_list)); From 87119b130bf7ec48181a33cd411f566d0234fc19 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 13 Sep 2016 20:14:22 +0300 Subject: [PATCH 123/179] cli/reply: only pass gmime message to add recipients to reply message The notmuch message is no longer needed. Simplify. --- notmuch-reply.c | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/notmuch-reply.c b/notmuch-reply.c index 5421ca80..8c894974 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -272,12 +272,11 @@ reply_to_header_is_redundant (GMimeMessage *message, return ret; } -static InternetAddressList *get_sender(unused(notmuch_message_t *message), - GMimeMessage *mime_message) +static InternetAddressList *get_sender(GMimeMessage *message) { const char *reply_to; - reply_to = g_mime_message_get_reply_to (mime_message); + reply_to = g_mime_message_get_reply_to (message); if (reply_to && *reply_to) { InternetAddressList *reply_to_list; @@ -295,35 +294,29 @@ static InternetAddressList *get_sender(unused(notmuch_message_t *message), * will always appear in the reply if reply_all is true. */ reply_to_list = internet_address_list_parse_string (reply_to); - if (! reply_to_header_is_redundant (mime_message, reply_to_list)) + if (! reply_to_header_is_redundant (message, reply_to_list)) return reply_to_list; g_object_unref (G_OBJECT (reply_to_list)); } return internet_address_list_parse_string ( - g_mime_message_get_sender (mime_message)); + g_mime_message_get_sender (message)); } -static InternetAddressList *get_to(unused(notmuch_message_t *message), - GMimeMessage *mime_message) +static InternetAddressList *get_to(GMimeMessage *message) { - return g_mime_message_get_recipients (mime_message, - GMIME_RECIPIENT_TYPE_TO); + return g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_TO); } -static InternetAddressList *get_cc(unused(notmuch_message_t *message), - GMimeMessage *mime_message) +static InternetAddressList *get_cc(GMimeMessage *message) { - return g_mime_message_get_recipients (mime_message, - GMIME_RECIPIENT_TYPE_CC); + return g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_CC); } -static InternetAddressList *get_bcc(unused(notmuch_message_t *message), - GMimeMessage *mime_message) +static InternetAddressList *get_bcc(GMimeMessage *message) { - return g_mime_message_get_recipients (mime_message, - GMIME_RECIPIENT_TYPE_BCC); + return g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_BCC); } /* Augment the recipients of 'reply' from the "Reply-to:", "From:", @@ -341,13 +334,11 @@ static InternetAddressList *get_bcc(unused(notmuch_message_t *message), static const char * add_recipients_from_message (GMimeMessage *reply, notmuch_config_t *config, - notmuch_message_t *message, - GMimeMessage *mime_message, + GMimeMessage *message, notmuch_bool_t reply_all) { struct { - InternetAddressList * (*get_header)(notmuch_message_t *message, - GMimeMessage *mime_message); + InternetAddressList * (*get_header)(GMimeMessage *message); GMimeRecipientType recipient_type; } reply_to_map[] = { { get_sender, GMIME_RECIPIENT_TYPE_TO }, @@ -362,7 +353,7 @@ add_recipients_from_message (GMimeMessage *reply, for (i = 0; i < ARRAY_SIZE (reply_to_map); i++) { InternetAddressList *recipients; - recipients = reply_to_map[i].get_header (message, mime_message); + recipients = reply_to_map[i].get_header (message); n += scan_address_list (recipients, config, reply, reply_to_map[i].recipient_type, &from_addr); @@ -567,7 +558,7 @@ create_reply_message(void *ctx, g_mime_object_set_header (GMIME_OBJECT (reply), "References", references); - from_addr = add_recipients_from_message (reply, config, message, + from_addr = add_recipients_from_message (reply, config, mime_message, reply_all); /* The above is all that is needed for limited headers. */ From 653c5ee2217adf4007bf76d2067948f6c603ae59 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Mon, 12 Sep 2016 22:45:57 -0300 Subject: [PATCH 124/179] test/crypto: test reply to encrypted message in emacs This test considers the extra output about encryption/signature status as a bug, to be fixed in the next commit --- test/T350-crypto.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/T350-crypto.sh b/test/T350-crypto.sh index 96349fa0..8e298979 100755 --- a/test/T350-crypto.sh +++ b/test/T350-crypto.sh @@ -316,6 +316,26 @@ test_expect_equal \ "$output" \ "$expected" +test_begin_subtest "Reply within emacs to an encrypted message" +test_subtest_known_broken +test_emacs "(let ((message-hidden-headers '()) + (notmuch-crypto-process-mime 't)) + (notmuch-show \"subject:test.encrypted.message.002\") + (notmuch-show-reply) + (test-output))" +# the empty To: is probably a bug, but it's not to do with encryption +grep -v -e '^In-Reply-To:' -e '^References:' -e '^Fcc:' -e 'To:' < OUTPUT > OUTPUT.clean +cat <EXPECTED +From: Notmuch Test Suite +Subject: Re: test encrypted message 002 +--text follows this line-- +<#secure method=pgpmime mode=signencrypt> +Notmuch Test Suite writes: + +> This is another test encrypted message. +EOF +test_expect_equal_file EXPECTED OUTPUT.clean + test_begin_subtest "signature verification with revoked key" # generate revocation certificate and load it to revoke key echo "y From a448879e49fb838a302b4e68d73e63c6b75d9175 Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Mon, 12 Sep 2016 22:45:58 -0300 Subject: [PATCH 125/179] emacs: reply: remove wrong sig/enc status buttons This stops the (usually incorrect) sigstatus and encstatus buttons appearing when replying in emacs, and updates the test suite to match. Overriding the status button functions is a little unusual but much less intrusive than passing an argument all the way down the call chain. It also makes it clear exactly what it does. We also hide the application/pgp-encrypted part as it can only contain "Version: 1". We do this in notmuch show, which means it also happens when replying. --- emacs/notmuch-mua.el | 7 +++++-- emacs/notmuch-show.el | 3 +++ test/T310-emacs.sh | 2 -- test/T350-crypto.sh | 1 - 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index fadf20fe..55bc2672 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -253,8 +253,11 @@ mutiple parts get a header." (notmuch-show-insert-header-p-function notmuch-mua-reply-insert-header-p-function) ;; Don't indent multipart sub-parts. (notmuch-show-indent-multipart nil)) - (notmuch-show-insert-body original (plist-get original :body) 0) - (buffer-substring-no-properties (point-min) (point-max))))) + ;; We don't want sigstatus buttons (an information leak and usually wrong anyway). + (letf (((symbol-function 'notmuch-crypto-insert-sigstatus-button) #'ignore) + ((symbol-function 'notmuch-crypto-insert-encstatus-button) #'ignore)) + (notmuch-show-insert-body original (plist-get original :body) 0) + (buffer-substring-no-properties (point-min) (point-max)))))) (set-mark (point)) (goto-char start) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index 5a585f38..641398dc 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -682,6 +682,9 @@ will return nil if the CID is unknown or cannot be retrieved." (indent-rigidly start (point) 1))) t) +(defun notmuch-show-insert-part-application/pgp-encrypted (msg part content-type nth depth button) + t) + (defun notmuch-show-insert-part-multipart/* (msg part content-type nth depth button) (let ((inner-parts (plist-get part :content)) (start (point))) diff --git a/test/T310-emacs.sh b/test/T310-emacs.sh index 202fc3bf..21675b68 100755 --- a/test/T310-emacs.sh +++ b/test/T310-emacs.sh @@ -384,8 +384,6 @@ References: <20091118002059.067214ed@hikari> --text follows this line-- Adrian Perez de Castro writes: -> [ Unknown signature status ] -> > Hello to all, > > I have just heard about Not Much today in some random Linux-related news diff --git a/test/T350-crypto.sh b/test/T350-crypto.sh index 8e298979..df2dc743 100755 --- a/test/T350-crypto.sh +++ b/test/T350-crypto.sh @@ -317,7 +317,6 @@ test_expect_equal \ "$expected" test_begin_subtest "Reply within emacs to an encrypted message" -test_subtest_known_broken test_emacs "(let ((message-hidden-headers '()) (notmuch-crypto-process-mime 't)) (notmuch-show \"subject:test.encrypted.message.002\") From a63e674b34ad8585f695da3a35d900a591d0dea4 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Sun, 11 Sep 2016 18:42:25 +0300 Subject: [PATCH 126/179] test: silence the output of notmuch new mid-test Fix this during test run: T470-missing-headers: Testing messages with missing headers Added 2 new messages to the database. --- test/T470-missing-headers.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/T470-missing-headers.sh b/test/T470-missing-headers.sh index e256c10a..256a885f 100755 --- a/test/T470-missing-headers.sh +++ b/test/T470-missing-headers.sh @@ -20,7 +20,7 @@ From: Notmuch Test Suite Body EOF -NOTMUCH_NEW +NOTMUCH_NEW >/dev/null test_begin_subtest "Search: text" output=$(notmuch search '*' | notmuch_search_sanitize) From 4dfb69169e6b685670ebdeedab898c31adc995b2 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 12 Jun 2016 22:05:48 -0300 Subject: [PATCH 127/179] lib: read "property" terms from messages. This is a first step towards providing an API to attach arbitrary (key,value) pairs to messages and retrieve all of the values for a given key. --- lib/database.cc | 1 + lib/message.cc | 29 ++++++++++++++++++++++++++++- lib/notmuch-private.h | 3 +++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/database.cc b/lib/database.cc index 5577aaf9..33e22d86 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -251,6 +251,7 @@ static prefix_t BOOLEAN_PREFIX_INTERNAL[] = { { "directory", "XDIRECTORY" }, { "file-direntry", "XFDIRENTRY" }, { "directory-direntry", "XDDIRENTRY" }, + { "property", "XPROPERTY" }, }; static prefix_t BOOLEAN_PREFIX_EXTERNAL[] = { diff --git a/lib/message.cc b/lib/message.cc index 24e698ab..63a8da56 100644 --- a/lib/message.cc +++ b/lib/message.cc @@ -37,6 +37,8 @@ struct visible _notmuch_message { notmuch_string_list_t *filename_list; char *author; notmuch_message_file_t *message_file; + notmuch_string_list_t *property_term_list; + notmuch_string_map_t *property_map; notmuch_message_list_t *replies; unsigned long flags; /* For flags that are initialized on-demand, lazy_flags indicates @@ -116,6 +118,8 @@ _notmuch_message_create_for_document (const void *talloc_owner, message->filename_list = NULL; message->message_file = NULL; message->author = NULL; + message->property_term_list = NULL; + message->property_map = NULL; message->replies = _notmuch_message_list_create (message); if (unlikely (message->replies == NULL)) { @@ -314,6 +318,7 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message) *id_prefix = _find_prefix ("id"), *type_prefix = _find_prefix ("type"), *filename_prefix = _find_prefix ("file-direntry"), + *property_prefix = _find_prefix ("property"), *replyto_prefix = _find_prefix ("replyto"); /* We do this all in a single pass because Xapian decompresses the @@ -369,11 +374,21 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message) _notmuch_database_get_terms_with_prefix (message, i, end, filename_prefix); + + /* Get property terms. Mimic the setup with filenames above */ + assert (strcmp (filename_prefix, property_prefix) < 0); + if (!message->property_map && !message->property_term_list) + message->property_term_list = + _notmuch_database_get_terms_with_prefix (message, i, end, + property_prefix); + /* Get reply to */ - assert (strcmp (filename_prefix, replyto_prefix) < 0); + assert (strcmp (property_prefix, replyto_prefix) < 0); if (!message->in_reply_to) message->in_reply_to = _notmuch_message_get_term (message, i, end, replyto_prefix); + + /* It's perfectly valid for a message to have no In-Reply-To * header. For these cases, we return an empty string. */ if (!message->in_reply_to) @@ -405,6 +420,18 @@ _notmuch_message_invalidate_metadata (notmuch_message_t *message, message->filename_term_list = message->filename_list = NULL; } + if (strcmp ("property", prefix_name) == 0) { + + if (message->property_term_list) + talloc_free (message->property_term_list); + message->property_term_list = NULL; + + if (message->property_map) + talloc_unlink (message, message->property_map); + + message->property_map = NULL; + } + if (strcmp ("replyto", prefix_name) == 0) { talloc_free (message->in_reply_to); message->in_reply_to = NULL; diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 643d9dd9..682b03d8 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -541,6 +541,9 @@ _notmuch_string_list_append (notmuch_string_list_t *list, void _notmuch_string_list_sort (notmuch_string_list_t *list); +/* string-map.c */ +typedef struct _notmuch_string_map notmuch_string_map_t; + /* tags.c */ notmuch_tags_t * From 8b03ee1d5a310f82718281362d105ff09e30148f Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 12 Jun 2016 22:05:49 -0300 Subject: [PATCH 128/179] lib: private string map (associative array) API The choice of array implementation is deliberate, for future iterator support --- lib/Makefile.local | 1 + lib/notmuch-private.h | 11 +++ lib/string-map.c | 153 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 lib/string-map.c diff --git a/lib/Makefile.local b/lib/Makefile.local index beb96358..b13513a2 100644 --- a/lib/Makefile.local +++ b/lib/Makefile.local @@ -40,6 +40,7 @@ libnotmuch_c_srcs = \ $(dir)/messages.c \ $(dir)/sha1.c \ $(dir)/built-with.c \ + $(dir)/string-map.c \ $(dir)/tags.c libnotmuch_cxx_srcs = \ diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 682b03d8..77011063 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -544,6 +544,17 @@ _notmuch_string_list_sort (notmuch_string_list_t *list); /* string-map.c */ typedef struct _notmuch_string_map notmuch_string_map_t; +notmuch_string_map_t * +_notmuch_string_map_create (const void *ctx); + +void +_notmuch_string_map_append (notmuch_string_map_t *map, + const char *key, + const char *value); + +const char * +_notmuch_string_map_get (notmuch_string_map_t *map, const char *key); + /* tags.c */ notmuch_tags_t * diff --git a/lib/string-map.c b/lib/string-map.c new file mode 100644 index 00000000..0491a10b --- /dev/null +++ b/lib/string-map.c @@ -0,0 +1,153 @@ +/* string-map.c - associative arrays of strings + * + * + * Copyright © 2016 David Bremner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/ . + * + * Author: David Bremner + */ + +#include "notmuch-private.h" + +/* Create a new notmuch_string_map_t object, with 'ctx' as its + * talloc owner. + * + * This function can return NULL in case of out-of-memory. + */ + +typedef struct _notmuch_string_pair_t { + char *key; + char *value; +} notmuch_string_pair_t; + +struct _notmuch_string_map { + notmuch_bool_t sorted; + size_t length; + notmuch_string_pair_t *pairs; +}; + +notmuch_string_map_t * +_notmuch_string_map_create (const void *ctx) +{ + notmuch_string_map_t *map; + + map = talloc (ctx, notmuch_string_map_t); + if (unlikely (map == NULL)) + return NULL; + + map->length = 0; + map->pairs = NULL; + map->sorted = TRUE; + + return map; +} + +void +_notmuch_string_map_append (notmuch_string_map_t *map, + const char *key, + const char *value) +{ + + map->length++; + map->sorted = FALSE; + + if (map->pairs) + map->pairs = talloc_realloc (map, map->pairs, notmuch_string_pair_t, map->length + 1); + else + map->pairs = talloc_array (map, notmuch_string_pair_t, map->length + 1); + + map->pairs[map->length - 1].key = talloc_strdup (map, key); + map->pairs[map->length - 1].value = talloc_strdup (map, value); + + /* Add sentinel */ + map->pairs[map->length].key = NULL; + map->pairs[map->length].value = NULL; + +} + +static int +cmppair (const void *pa, const void *pb) +{ + notmuch_string_pair_t *a = (notmuch_string_pair_t *) pa; + notmuch_string_pair_t *b = (notmuch_string_pair_t *) pb; + + return strcmp (a->key, b->key); +} + +static void +_notmuch_string_map_sort (notmuch_string_map_t *map) +{ + if (map->length == 0) + return; + + if (map->sorted) + return; + + qsort (map->pairs, map->length, sizeof (notmuch_string_pair_t), cmppair); + + map->sorted = TRUE; +} + +static notmuch_bool_t +string_cmp (const char *a, const char *b, notmuch_bool_t exact) +{ + if (exact) + return (strcmp (a, b)); + else + return (strncmp (a, b, strlen (a))); +} + +static notmuch_string_pair_t * +bsearch_first (notmuch_string_pair_t *array, size_t len, const char *key, notmuch_bool_t exact) +{ + size_t first = 0; + size_t last = len - 1; + size_t mid; + + if (len <= 0) + return NULL; + + while (last > first) { + mid = (first + last) / 2; + int sign = string_cmp (key, array[mid].key, exact); + + if (sign <= 0) + last = mid; + else + first = mid + 1; + } + + + if (string_cmp (key, array[first].key, exact) == 0) + return array + first; + else + return NULL; + +} + +const char * +_notmuch_string_map_get (notmuch_string_map_t *map, const char *key) +{ + notmuch_string_pair_t *pair; + + /* this means that calling append invalidates iterators */ + _notmuch_string_map_sort (map); + + pair = bsearch_first (map->pairs, map->length, key, TRUE); + if (! pair) + return NULL; + + return pair->value; +} From b8bb6d796458732622f80464dd808b3e02f57d9d Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 12 Jun 2016 22:05:50 -0300 Subject: [PATCH 129/179] lib: basic message-property API Initially, support get, set and removal of single key/value pair, as well as removing all properties. --- lib/Makefile.local | 1 + lib/message-private.h | 16 +++++ lib/message-property.cc | 108 ++++++++++++++++++++++++++++++++++ lib/message.cc | 52 +++++++++++++++- lib/notmuch.h | 72 +++++++++++++++++++++++ test/T610-message-property.sh | 84 ++++++++++++++++++++++++++ 6 files changed, 331 insertions(+), 2 deletions(-) create mode 100644 lib/message-private.h create mode 100644 lib/message-property.cc create mode 100755 test/T610-message-property.sh diff --git a/lib/Makefile.local b/lib/Makefile.local index b13513a2..3d1030a5 100644 --- a/lib/Makefile.local +++ b/lib/Makefile.local @@ -49,6 +49,7 @@ libnotmuch_cxx_srcs = \ $(dir)/directory.cc \ $(dir)/index.cc \ $(dir)/message.cc \ + $(dir)/message-property.cc \ $(dir)/query.cc \ $(dir)/query-fp.cc \ $(dir)/config.cc \ diff --git a/lib/message-private.h b/lib/message-private.h new file mode 100644 index 00000000..74199256 --- /dev/null +++ b/lib/message-private.h @@ -0,0 +1,16 @@ +#ifndef MESSAGE_PRIVATE_H +#define MESSAGE_PRIVATE_H + +notmuch_string_map_t * +_notmuch_message_property_map (notmuch_message_t *message); + +notmuch_bool_t +_notmuch_message_frozen (notmuch_message_t *message); + +void +_notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix); + +void +_notmuch_message_invalidate_metadata (notmuch_message_t *message, const char *prefix_name); + +#endif diff --git a/lib/message-property.cc b/lib/message-property.cc new file mode 100644 index 00000000..1f04a207 --- /dev/null +++ b/lib/message-property.cc @@ -0,0 +1,108 @@ +/* message-property.cc - Properties are like tags, but (key,value) pairs. + * keys are allowed to repeat. + * + * This file is part of notmuch. + * + * Copyright © 2016 David Bremner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/ . + * + * Author: David Bremner + */ + +#include "notmuch-private.h" +#include "database-private.h" +#include "message-private.h" + +notmuch_status_t +notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value) +{ + if (! value) + return NOTMUCH_STATUS_NULL_POINTER; + + *value = _notmuch_string_map_get (_notmuch_message_property_map (message), key); + + return NOTMUCH_STATUS_SUCCESS; +} + +static notmuch_status_t +_notmuch_message_modify_property (notmuch_message_t *message, const char *key, const char *value, + notmuch_bool_t delete_it) +{ + notmuch_private_status_t private_status; + notmuch_status_t status; + char *term = NULL; + + status = _notmuch_database_ensure_writable (_notmuch_message_database (message)); + if (status) + return status; + + if (key == NULL || value == NULL) + return NOTMUCH_STATUS_NULL_POINTER; + + if (index (key, '=')) + return NOTMUCH_STATUS_ILLEGAL_ARGUMENT; + + term = talloc_asprintf (message, "%s=%s", key, value); + + if (delete_it) + private_status = _notmuch_message_remove_term (message, "property", term); + else + private_status = _notmuch_message_add_term (message, "property", term); + + if (private_status) + return COERCE_STATUS (private_status, + "Unhandled error modifying message property"); + if (! _notmuch_message_frozen (message)) + _notmuch_message_sync (message); + + if (term) + talloc_free (term); + + return NOTMUCH_STATUS_SUCCESS; +} + +notmuch_status_t +notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value) +{ + return _notmuch_message_modify_property (message, key, value, FALSE); +} + +notmuch_status_t +notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value) +{ + return _notmuch_message_modify_property (message, key, value, TRUE); +} + +notmuch_status_t +notmuch_message_remove_all_properties (notmuch_message_t *message, const char *key) +{ + notmuch_status_t status; + const char * term_prefix; + + status = _notmuch_database_ensure_writable (_notmuch_message_database (message)); + if (status) + return status; + + _notmuch_message_invalidate_metadata (message, "property"); + if (key) + term_prefix = talloc_asprintf (message, "%s%s=", _find_prefix ("property"), key); + else + term_prefix = _find_prefix ("property"); + + /* XXX better error reporting ? */ + _notmuch_message_remove_terms (message, term_prefix); + + return NOTMUCH_STATUS_SUCCESS; +} diff --git a/lib/message.cc b/lib/message.cc index 63a8da56..9d3e8071 100644 --- a/lib/message.cc +++ b/lib/message.cc @@ -20,6 +20,7 @@ #include "notmuch-private.h" #include "database-private.h" +#include "message-private.h" #include @@ -395,7 +396,7 @@ _notmuch_message_ensure_metadata (notmuch_message_t *message) message->in_reply_to = talloc_strdup (message, ""); } -static void +void _notmuch_message_invalidate_metadata (notmuch_message_t *message, const char *prefix_name) { @@ -552,7 +553,7 @@ notmuch_message_get_replies (notmuch_message_t *message) return _notmuch_messages_create (message->replies); } -static void +void _notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix) { Xapian::TermIterator i; @@ -1799,3 +1800,50 @@ _notmuch_message_database (notmuch_message_t *message) { return message->notmuch; } + +void +_notmuch_message_ensure_property_map (notmuch_message_t *message) +{ + notmuch_string_node_t *node; + + if (message->property_map) + return; + + if (!message->property_term_list) + _notmuch_message_ensure_metadata (message); + + message->property_map = _notmuch_string_map_create (message); + + for (node = message->property_term_list->head; node; node = node->next) { + const char *key; + char *value; + + value = index(node->string, '='); + if (!value) + INTERNAL_ERROR ("malformed property term"); + + *value = '\0'; + value++; + key = node->string; + + _notmuch_string_map_append (message->property_map, key, value); + + } + + talloc_free (message->property_term_list); + message->property_term_list = NULL; +} + +notmuch_string_map_t * +_notmuch_message_property_map (notmuch_message_t *message) +{ + _notmuch_message_ensure_property_map (message); + + return message->property_map; +} + +notmuch_bool_t +_notmuch_message_frozen (notmuch_message_t *message) +{ + return message->frozen; +} diff --git a/lib/notmuch.h b/lib/notmuch.h index 2faa1468..b304dcac 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -179,6 +179,11 @@ typedef enum _notmuch_status { * passed to a function expecting an absolute path. */ NOTMUCH_STATUS_PATH_ERROR, + /** + * One of the arguments violates the preconditions for the + * function, in a way not covered by a more specific argument. + */ + NOTMUCH_STATUS_ILLEGAL_ARGUMENT, /** * Not an actual status value. Just a way to find out how many * valid status values there are. @@ -1653,6 +1658,73 @@ notmuch_message_thaw (notmuch_message_t *message); void notmuch_message_destroy (notmuch_message_t *message); +/** + * @name Message Properties + * + * This interface provides the ability to attach arbitrary (key,value) + * string pairs to a message, to remove such pairs, and to iterate + * over them. The caller should take some care as to what keys they + * add or delete values for, as other subsystems or extensions may + * depend on these properties. + * + */ +/**@{*/ +/** + * Retrieve the value for a single property key + * + * *value* is set to a string owned by the message or NULL if there is + * no such key. In the case of multiple values for the given key, the + * first one is retrieved. + * + * @returns + * - NOTMUCH_STATUS_NULL_POINTER: *value* may not be NULL. + * - NOTMUCH_STATUS_SUCCESS: No error occured. + + */ +notmuch_status_t +notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value); + +/** + * Add a (key,value) pair to a message + * + * @returns + * - NOTMUCH_STATUS_ILLEGAL_ARGUMENT: *key* may not contain an '=' character. + * - NOTMUCH_STATUS_NULL_POINTER: Neither *key* nor *value* may be NULL. + * - NOTMUCH_STATUS_SUCCESS: No error occured. + */ +notmuch_status_t +notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value); + +/** + * Remove a (key,value) pair from a message. + * + * It is not an error to remove a non-existant (key,value) pair + * + * @returns + * - NOTMUCH_STATUS_ILLEGAL_ARGUMENT: *key* may not contain an '=' character. + * - NOTMUCH_STATUS_NULL_POINTER: Neither *key* nor *value* may be NULL. + * - NOTMUCH_STATUS_SUCCESS: No error occured. + */ +notmuch_status_t +notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value); + +/** + * Remove all (key,value) pairs from the given message. + * + * @param[in,out] message message to operate on. + * @param[in] key key to delete properties for. If NULL, delete + * properties for all keys + * @returns + * - NOTMUCH_STATUS_READ_ONLY_DATABASE: Database was opened in + * read-only mode so message cannot be modified. + * - NOTMUCH_STATUS_SUCCESS: No error occured. + * + */ +notmuch_status_t +notmuch_message_remove_all_properties (notmuch_message_t *message, const char *key); + +/**@}*/ + /** * Is the given 'tags' iterator pointing at a valid tag. * diff --git a/test/T610-message-property.sh b/test/T610-message-property.sh new file mode 100755 index 00000000..0217950f --- /dev/null +++ b/test/T610-message-property.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +test_description="message property API" + +. ./test-lib.sh || exit 1 + +add_email_corpus + +cat < c_head +#include +#include +#include +#include + +int main (int argc, char** argv) +{ + notmuch_database_t *db; + notmuch_message_t *message = NULL; + const char *val; + notmuch_status_t stat; + + EXPECT0(notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db)); + EXPECT0(notmuch_database_find_message(db, "4EFC743A.3060609@april.org", &message)); + if (message == NULL) { + fprintf (stderr, "unable to find message"); + exit (1); + } +EOF + +cat < c_tail + EXPECT0(notmuch_database_destroy(db)); +} +EOF + +test_begin_subtest "notmuch_message_{add,get,remove}_property" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +{ + EXPECT0(notmuch_message_add_property (message, "testkey1", "testvalue1")); + EXPECT0(notmuch_message_get_property (message, "testkey1", &val)); + printf("testkey1[1] = %s\n", val); + EXPECT0(notmuch_message_add_property (message, "testkey2", "this value has spaces and = sign")); + EXPECT0(notmuch_message_get_property (message, "testkey1", &val)); + printf("testkey1[2] = %s\n", val); + EXPECT0(notmuch_message_get_property (message, "testkey1", &val)); + + EXPECT0(notmuch_message_get_property (message, "testkey2", &val)); + printf("testkey2 = %s\n", val); + + /* Add second value for key */ + EXPECT0(notmuch_message_add_property (message, "testkey2", "zztestvalue3")); + EXPECT0(notmuch_message_get_property (message, "testkey2", &val)); + printf("testkey2 = %s\n", val); + + /* remove first value for key */ + EXPECT0(notmuch_message_remove_property (message, "testkey2", "this value has spaces and = sign")); + EXPECT0(notmuch_message_get_property (message, "testkey2", &val)); + printf("testkey2 = %s\n", val); + + /* remove non-existant value for key */ + EXPECT0(notmuch_message_remove_property (message, "testkey2", "this value has spaces and = sign")); + EXPECT0(notmuch_message_get_property (message, "testkey2", &val)); + printf("testkey2 = %s\n", val); + + /* remove only value for key */ + EXPECT0(notmuch_message_remove_property (message, "testkey2", "zztestvalue3")); + EXPECT0(notmuch_message_get_property (message, "testkey2", &val)); + printf("testkey2 = %s\n", val == NULL ? "NULL" : val); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +testkey1[1] = testvalue1 +testkey1[2] = testvalue1 +testkey2 = this value has spaces and = sign +testkey2 = this value has spaces and = sign +testkey2 = zztestvalue3 +testkey2 = zztestvalue3 +testkey2 = NULL +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT + + + +test_done From b846bdb48233c907d1ecaff495fbf6bc578910d6 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 12 Jun 2016 22:05:51 -0300 Subject: [PATCH 130/179] lib: extend private string map API with iterators Support for prefix based iterators is perhaps overengineering, but I wanted to mimic the existing database_config API. --- lib/notmuch-private.h | 21 ++++++++++++- lib/string-map.c | 72 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h index 77011063..7b35fc5b 100644 --- a/lib/notmuch-private.h +++ b/lib/notmuch-private.h @@ -543,7 +543,7 @@ _notmuch_string_list_sort (notmuch_string_list_t *list); /* string-map.c */ typedef struct _notmuch_string_map notmuch_string_map_t; - +typedef struct _notmuch_string_map_iterator notmuch_string_map_iterator_t; notmuch_string_map_t * _notmuch_string_map_create (const void *ctx); @@ -555,6 +555,25 @@ _notmuch_string_map_append (notmuch_string_map_t *map, const char * _notmuch_string_map_get (notmuch_string_map_t *map, const char *key); +notmuch_string_map_iterator_t * +_notmuch_string_map_iterator_create (notmuch_string_map_t *map, const char *key, + notmuch_bool_t exact); + +notmuch_bool_t +_notmuch_string_map_iterator_valid (notmuch_string_map_iterator_t *iter); + +void +_notmuch_string_map_iterator_move_to_next (notmuch_string_map_iterator_t *iter); + +const char * +_notmuch_string_map_iterator_key (notmuch_string_map_iterator_t *iterator); + +const char * +_notmuch_string_map_iterator_value (notmuch_string_map_iterator_t *iterator); + +void +_notmuch_string_map_iterator_destroy (notmuch_string_map_iterator_t *iterator); + /* tags.c */ notmuch_tags_t * diff --git a/lib/string-map.c b/lib/string-map.c index 0491a10b..591ff6d5 100644 --- a/lib/string-map.c +++ b/lib/string-map.c @@ -38,6 +38,12 @@ struct _notmuch_string_map { notmuch_string_pair_t *pairs; }; +struct _notmuch_string_map_iterator { + notmuch_string_pair_t *current; + notmuch_bool_t exact; + const char *key; +}; + notmuch_string_map_t * _notmuch_string_map_create (const void *ctx) { @@ -151,3 +157,69 @@ _notmuch_string_map_get (notmuch_string_map_t *map, const char *key) return pair->value; } + +notmuch_string_map_iterator_t * +_notmuch_string_map_iterator_create (notmuch_string_map_t *map, const char *key, + notmuch_bool_t exact) +{ + notmuch_string_map_iterator_t *iter; + + _notmuch_string_map_sort (map); + + iter = talloc (map, notmuch_string_map_iterator_t); + if (unlikely (iter == NULL)) + return NULL; + + iter->key = talloc_strdup (iter, key); + iter->exact = exact; + iter->current = bsearch_first (map->pairs, map->length, key, exact); + return iter; +} + +notmuch_bool_t +_notmuch_string_map_iterator_valid (notmuch_string_map_iterator_t *iterator) +{ + if (iterator->current == NULL) + return FALSE; + + /* sentinel */ + if (iterator->current->key == NULL) + return FALSE; + + return (0 == string_cmp (iterator->key, iterator->current->key, iterator->exact)); + +} + +void +_notmuch_string_map_iterator_move_to_next (notmuch_string_map_iterator_t *iterator) +{ + + if (! _notmuch_string_map_iterator_valid (iterator)) + return; + + (iterator->current)++; +} + +const char * +_notmuch_string_map_iterator_key (notmuch_string_map_iterator_t *iterator) +{ + if (! _notmuch_string_map_iterator_valid (iterator)) + return NULL; + + return iterator->current->key; +} + +const char * +_notmuch_string_map_iterator_value (notmuch_string_map_iterator_t *iterator) +{ + if (! _notmuch_string_map_iterator_valid (iterator)) + return NULL; + + return iterator->current->value; +} + +void +_notmuch_string_map_iterator_destroy (notmuch_string_map_iterator_t *iterator) +{ + talloc_free (iterator); +} From 58fe8fce1d1b7b86b4914a5a476ff21411c84082 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 12 Jun 2016 22:05:52 -0300 Subject: [PATCH 131/179] lib: iterator API for message properties This is a thin wrapper around the string map iterator API just introduced. --- lib/message-property.cc | 38 ++++++++++++ lib/notmuch.h | 95 ++++++++++++++++++++++++++++++ test/T610-message-property.sh | 107 ++++++++++++++++++++++++++++++++++ 3 files changed, 240 insertions(+) diff --git a/lib/message-property.cc b/lib/message-property.cc index 1f04a207..0b13cac3 100644 --- a/lib/message-property.cc +++ b/lib/message-property.cc @@ -106,3 +106,41 @@ notmuch_message_remove_all_properties (notmuch_message_t *message, const char *k return NOTMUCH_STATUS_SUCCESS; } + +notmuch_message_properties_t * +notmuch_message_get_properties (notmuch_message_t *message, const char *key, notmuch_bool_t exact) +{ + notmuch_string_map_t *map; + map = _notmuch_message_property_map (message); + return _notmuch_string_map_iterator_create (map, key, exact); +} + +notmuch_bool_t +notmuch_message_properties_valid (notmuch_message_properties_t *properties) +{ + return _notmuch_string_map_iterator_valid (properties); +} + +void +notmuch_message_properties_move_to_next (notmuch_message_properties_t *properties) +{ + return _notmuch_string_map_iterator_move_to_next (properties); +} + +const char * +notmuch_message_properties_key (notmuch_message_properties_t *properties) +{ + return _notmuch_string_map_iterator_key (properties); +} + +const char * +notmuch_message_properties_value (notmuch_message_properties_t *properties) +{ + return _notmuch_string_map_iterator_value (properties); +} + +void +notmuch_message_properties_destroy (notmuch_message_properties_t *properties) +{ + _notmuch_string_map_iterator_destroy (properties); +} diff --git a/lib/notmuch.h b/lib/notmuch.h index b304dcac..e03a05dd 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -1723,6 +1723,101 @@ 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); +/** + * Opaque message property iterator + */ +typedef struct _notmuch_string_map_iterator notmuch_message_properties_t; + +/** + * Get the properties for *message*, returning a + * notmuch_message_properties_t object which can be used to iterate + * over all properties. + * + * The notmuch_message_properties_t object is owned by the message and + * as such, will only be valid for as long as the message is valid, + * (which is until the query from which it derived is destroyed). + * + * @param[in] message The message to examine + * @param[in] key key or key prefix + * @param[in] exact if TRUE, require exact match with key. Otherwise + * treat as prefix. + * + * Typical usage might be: + * + * notmuch_message_properties_t *list; + * + * for (list = notmuch_message_get_properties (message, "testkey1", TRUE); + * notmuch_message_properties_valid (list); notmuch_message_properties_move_to_next (list)) { + * printf("%s\n", notmuch_message_properties_value(list)); + * } + * + * notmuch_message_properties_destroy (list); + * + * Note that there's no explicit destructor needed for the + * notmuch_message_properties_t object. (For consistency, we do + * provide a notmuch_message_properities_destroy function, but there's + * no good reason to call it if the message is about to be destroyed). + */ +notmuch_message_properties_t * +notmuch_message_get_properties (notmuch_message_t *message, const char *key, notmuch_bool_t exact); + +/** + * Is the given *properties* iterator pointing at a valid (key,value) + * pair. + * + * When this function returns TRUE, + * notmuch_message_properties_{key,value} will return a valid string, + * and notmuch_message_properties_move_to_next will do what it + * says. Whereas when this function returns FALSE, calling any of + * these functions results in undefined behaviour. + * + * See the documentation of notmuch_message_properties_get for example + * code showing how to iterate over a notmuch_message_properties_t + * object. + */ +notmuch_bool_t +notmuch_message_properties_valid (notmuch_message_properties_t *properties); + +/** + * Move the *properties* iterator to the next (key,value) pair + * + * If *properties* is already pointing at the last pair then the iterator + * will be moved to a point just beyond that last pair, (where + * notmuch_message_properties_valid will return FALSE). + * + * See the documentation of notmuch_message_get_properties for example + * code showing how to iterate over a notmuch_message_properties_t object. + */ +void +notmuch_message_properties_move_to_next (notmuch_message_properties_t *properties); + +/** + * Return the key from the current (key,value) pair. + * + * this could be useful if iterating for a prefix + */ +const char * +notmuch_message_properties_key (notmuch_message_properties_t *properties); + +/** + * Return the key from the current (key,value) pair. + * + * This could be useful if iterating for a prefix. + */ +const char * +notmuch_message_properties_value (notmuch_message_properties_t *properties); + + +/** + * Destroy a notmuch_message_properties_t object. + * + * It's not strictly necessary to call this function. All memory from + * the notmuch_message_properties_t object will be reclaimed when the + * containing message object is destroyed. + */ +void +notmuch_message_properties_destroy (notmuch_message_properties_t *properties); + /**@}*/ /** diff --git a/test/T610-message-property.sh b/test/T610-message-property.sh index 0217950f..b5ddb7a4 100755 --- a/test/T610-message-property.sh +++ b/test/T610-message-property.sh @@ -9,8 +9,18 @@ cat < c_head #include #include #include +#include #include +void print_properties (notmuch_message_t *message, const char *prefix, notmuch_bool_t exact) { + notmuch_message_properties_t *list; + for (list = notmuch_message_get_properties (message, prefix, exact); + notmuch_message_properties_valid (list); notmuch_message_properties_move_to_next (list)) { + printf("%s\n", notmuch_message_properties_value(list)); + } + notmuch_message_properties_destroy (list); +} + int main (int argc, char** argv) { notmuch_database_t *db; @@ -79,6 +89,103 @@ testkey2 = NULL EOF test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "notmuch_message_get_properties: empty list" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +{ + notmuch_message_properties_t *list; + list = notmuch_message_get_properties (message, "nonexistent", TRUE); + printf("valid = %d\n", notmuch_message_properties_valid (list)); + notmuch_message_properties_destroy (list); +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +valid = 0 +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "notmuch_message_properties: one value" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +print_properties (message, "testkey1", TRUE); +EOF +cat <<'EOF' >EXPECTED +== stdout == +testvalue1 +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "notmuch_message_properties: multiple values" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +EXPECT0(notmuch_message_add_property (message, "testkey1", "bob")); +EXPECT0(notmuch_message_add_property (message, "testkey1", "testvalue2")); +EXPECT0(notmuch_message_add_property (message, "testkey1", "alice")); +print_properties (message, "testkey1", TRUE); +EOF +cat <<'EOF' >EXPECTED +== stdout == +alice +bob +testvalue1 +testvalue2 +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "notmuch_message_properties: prefix" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +EXPECT0(notmuch_message_add_property (message, "testkey3", "bob3")); +EXPECT0(notmuch_message_add_property (message, "testkey3", "testvalue3")); +EXPECT0(notmuch_message_add_property (message, "testkey3", "alice3")); +print_properties (message, "testkey", FALSE); +EOF +cat <<'EOF' >EXPECTED +== stdout == +alice +bob +testvalue1 +testvalue2 +alice3 +bob3 +testvalue3 +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "notmuch_message_properties: modify during iteration" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +{ + const char *keys[1000] = {NULL}; + const char *vals[1000] = {NULL}; + notmuch_message_properties_t *properties; + int i; + + for (properties = notmuch_message_get_properties (message, "", FALSE), i=0; + notmuch_message_properties_valid (properties); + notmuch_message_properties_move_to_next (properties), i++) + { + const char *key, *value; + + keys[i]=talloc_strdup(message, + notmuch_message_properties_key (properties)); + vals[i]=talloc_strdup(message, + notmuch_message_properties_value (properties)); + + EXPECT0(notmuch_message_remove_property (message, keys[i], vals[i])); + } + + print_properties (message, "", FALSE); + + for (i = 0; keys[i] && vals[i]; i++) { + EXPECT0(notmuch_message_add_property (message, keys[i], vals[i])); + } +} +EOF +cat <<'EOF' >EXPECTED +== stdout == +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT test_done From f240528095169bf45a88e6790fd6b546cc8f48c5 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 12 Jun 2016 22:05:53 -0300 Subject: [PATCH 132/179] CLI: refactor dumping of tags. This is mainly code movement, to make room in the loop over messages for dumping properties. --- notmuch-dump.c | 127 +++++++++++++++++++++++++++---------------------- 1 file changed, 69 insertions(+), 58 deletions(-) diff --git a/notmuch-dump.c b/notmuch-dump.c index cae1db8a..d80ed8b8 100644 --- a/notmuch-dump.c +++ b/notmuch-dump.c @@ -77,6 +77,69 @@ print_dump_header (gzFile output, int output_format, int include) (include & DUMP_INCLUDE_TAGS) ? "tags" : ""); } +static int +dump_tags_message (void *ctx, + notmuch_message_t *message, int output_format, + gzFile output, + char **buffer_p, size_t *size_p) +{ + int first = 1; + const char *message_id; + + message_id = notmuch_message_get_message_id (message); + + if (output_format == DUMP_FORMAT_BATCH_TAG && + strchr (message_id, '\n')) { + /* This will produce a line break in the output, which + * would be difficult to handle in tools. However, it's + * also impossible to produce an email containing a line + * break in a message ID because of unfolding, so we can + * safely disallow it. */ + fprintf (stderr, "Warning: skipping message id containing line break: \"%s\"\n", message_id); + return EXIT_SUCCESS; + } + + if (output_format == DUMP_FORMAT_SUP) { + gzprintf (output, "%s (", message_id); + } + + for (notmuch_tags_t *tags = notmuch_message_get_tags (message); + notmuch_tags_valid (tags); + notmuch_tags_move_to_next (tags)) { + const char *tag_str = notmuch_tags_get (tags); + + if (! first) + gzputs (output, " "); + + first = 0; + + if (output_format == DUMP_FORMAT_SUP) { + gzputs (output, tag_str); + } else { + if (hex_encode (ctx, tag_str, + buffer_p, size_p) != HEX_SUCCESS) { + fprintf (stderr, "Error: failed to hex-encode tag %s\n", + tag_str); + return EXIT_FAILURE; + } + gzprintf (output, "+%s", *buffer_p); + } + } + + if (output_format == DUMP_FORMAT_SUP) { + gzputs (output, ")\n"); + } else { + if (make_boolean_term (ctx, "id", message_id, + buffer_p, size_p)) { + fprintf (stderr, "Error quoting message id %s: %s\n", + message_id, strerror (errno)); + return EXIT_FAILURE; + } + gzprintf (output, " -- %s\n", *buffer_p); + } + return EXIT_SUCCESS; +} + static int database_dump_file (notmuch_database_t *notmuch, gzFile output, const char *query_str, int output_format, int include) @@ -84,7 +147,9 @@ database_dump_file (notmuch_database_t *notmuch, gzFile output, notmuch_query_t *query; notmuch_messages_t *messages; notmuch_message_t *message; - notmuch_tags_t *tags; + notmuch_status_t status; + char *buffer = NULL; + size_t buffer_size = 0; print_dump_header (output, output_format, include); @@ -110,10 +175,6 @@ database_dump_file (notmuch_database_t *notmuch, gzFile output, */ notmuch_query_set_sort (query, NOTMUCH_SORT_UNSORTED); - char *buffer = NULL; - size_t buffer_size = 0; - notmuch_status_t status; - status = notmuch_query_search_messages_st (query, &messages); if (print_status_query ("notmuch dump", query, status)) return EXIT_FAILURE; @@ -121,62 +182,12 @@ database_dump_file (notmuch_database_t *notmuch, gzFile output, for (; notmuch_messages_valid (messages); notmuch_messages_move_to_next (messages)) { - int first = 1; - const char *message_id; message = notmuch_messages_get (messages); - message_id = notmuch_message_get_message_id (message); - if (output_format == DUMP_FORMAT_BATCH_TAG && - strchr (message_id, '\n')) { - /* This will produce a line break in the output, which - * would be difficult to handle in tools. However, it's - * also impossible to produce an email containing a line - * break in a message ID because of unfolding, so we can - * safely disallow it. */ - fprintf (stderr, "Warning: skipping message id containing line break: \"%s\"\n", message_id); - notmuch_message_destroy (message); - continue; - } - - if (output_format == DUMP_FORMAT_SUP) { - gzprintf (output, "%s (", message_id); - } - - for (tags = notmuch_message_get_tags (message); - notmuch_tags_valid (tags); - notmuch_tags_move_to_next (tags)) { - const char *tag_str = notmuch_tags_get (tags); - - if (! first) - gzputs (output, " "); - - first = 0; - - if (output_format == DUMP_FORMAT_SUP) { - gzputs (output, tag_str); - } else { - if (hex_encode (notmuch, tag_str, - &buffer, &buffer_size) != HEX_SUCCESS) { - fprintf (stderr, "Error: failed to hex-encode tag %s\n", - tag_str); - return EXIT_FAILURE; - } - gzprintf (output, "+%s", buffer); - } - } - - if (output_format == DUMP_FORMAT_SUP) { - gzputs (output, ")\n"); - } else { - if (make_boolean_term (notmuch, "id", message_id, - &buffer, &buffer_size)) { - fprintf (stderr, "Error quoting message id %s: %s\n", - message_id, strerror (errno)); - return EXIT_FAILURE; - } - gzprintf (output, " -- %s\n", buffer); - } + if (dump_tags_message (notmuch, message, output_format, output, + &buffer, &buffer_size)) + return EXIT_FAILURE; notmuch_message_destroy (message); } From b7345d277ec5f562000c0e740e6515c2a84f9c76 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 12 Jun 2016 22:05:54 -0300 Subject: [PATCH 133/179] CLI: add properties to dump output Part of providing extensibility via properties is to make sure that user data is not lost. Thus we need to be able to dump and restore properties. --- doc/man1/notmuch-dump.rst | 18 +++++--- notmuch-client.h | 3 ++ notmuch-dump.c | 85 ++++++++++++++++++++++++++++++++--- notmuch-new.c | 2 +- test/T610-message-property.sh | 21 +++++++++ 5 files changed, 116 insertions(+), 13 deletions(-) diff --git a/doc/man1/notmuch-dump.rst b/doc/man1/notmuch-dump.rst index d56974ed..58570272 100644 --- a/doc/man1/notmuch-dump.rst +++ b/doc/man1/notmuch-dump.rst @@ -71,7 +71,7 @@ Supported options for **dump** include characters. Note also that tags with spaces will not be correctly restored with this format. - ``--include=(config|tags)`` + ``--include=(config|properties|tags)`` Control what kind of metadata is included in the output. @@ -81,14 +81,22 @@ Supported options for **dump** include starts with "#@ ", followed by a space separated key-value pair. Both key and value are hex encoded if needed. + **properties** + + Output per-message (key,value) metadata. Each line starts + with "#= ", followed by a message id, and a space separated + list of key=value pairs. pair. Ids, keys and values are hex + encoded if needed. + **tags** - Output per-message metadata, namely tags. See *format* above + Output per-message boolean metadata, namely tags. See *format* above for description of the output. - The default is to include both tags and configuration - information. As of version 2 of the dump format, there is a - header line of the following form + The default is to include all available types of data. The + option can be specified multiple times to select some subset. As + of version 2 of the dump format, there is a header line of the + following form | | #notmuch-dump <*format*>:<*version*> <*included*> diff --git a/notmuch-client.h b/notmuch-client.h index ebc092b8..9ce2aef1 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -449,8 +449,11 @@ typedef enum dump_formats { typedef enum dump_includes { DUMP_INCLUDE_TAGS = 1, DUMP_INCLUDE_CONFIG = 2, + DUMP_INCLUDE_PROPERTIES = 4 } dump_include_t; +#define DUMP_INCLUDE_DEFAULT (DUMP_INCLUDE_TAGS | DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_PROPERTIES) + #define NOTMUCH_DUMP_VERSION 2 int diff --git a/notmuch-dump.c b/notmuch-dump.c index d80ed8b8..e7965cea 100644 --- a/notmuch-dump.c +++ b/notmuch-dump.c @@ -69,12 +69,77 @@ database_dump_config (notmuch_database_t *notmuch, gzFile output) static void print_dump_header (gzFile output, int output_format, int include) { - gzprintf (output, "#notmuch-dump %s:%d %s%s%s\n", + const char *sep = ""; + + gzprintf (output, "#notmuch-dump %s:%d ", (output_format == DUMP_FORMAT_SUP) ? "sup" : "batch-tag", - NOTMUCH_DUMP_VERSION, - (include & DUMP_INCLUDE_CONFIG) ? "config" : "", - (include & DUMP_INCLUDE_TAGS) && (include & DUMP_INCLUDE_CONFIG) ? "," : "", - (include & DUMP_INCLUDE_TAGS) ? "tags" : ""); + NOTMUCH_DUMP_VERSION); + + if (include & DUMP_INCLUDE_CONFIG) { + gzputs (output, "config"); + sep = ","; + } + if (include & DUMP_INCLUDE_PROPERTIES) { + gzprintf (output, "%sproperties", sep); + sep = ","; + } + if (include & DUMP_INCLUDE_TAGS) { + gzprintf (output, "%sproperties", sep); + } + gzputs (output, "\n"); +} + +static int +dump_properties_message (void *ctx, + notmuch_message_t *message, + gzFile output, + char **buffer_p, size_t *size_p) +{ + const char *message_id; + notmuch_message_properties_t *list; + notmuch_bool_t first = TRUE; + + message_id = notmuch_message_get_message_id (message); + + if (strchr (message_id, '\n')) { + fprintf (stderr, "Warning: skipping message id containing line break: \"%s\"\n", message_id); + return 0; + } + + for (list = notmuch_message_get_properties (message, "", FALSE); + notmuch_message_properties_valid (list); notmuch_message_properties_move_to_next (list)) { + const char *key, *val; + + if (first) { + if (hex_encode (ctx, message_id, buffer_p, size_p) != HEX_SUCCESS) { + fprintf (stderr, "Error: failed to hex-encode message-id %s\n", message_id); + return 1; + } + gzprintf (output, "#= %s", *buffer_p); + first = FALSE; + } + + key = notmuch_message_properties_key (list); + val = notmuch_message_properties_value (list); + + if (hex_encode (ctx, key, buffer_p, size_p) != HEX_SUCCESS) { + fprintf (stderr, "Error: failed to hex-encode key %s\n", key); + return 1; + } + gzprintf (output, " %s", *buffer_p); + + if (hex_encode (ctx, val, buffer_p, size_p) != HEX_SUCCESS) { + fprintf (stderr, "Error: failed to hex-encode value %s\n", val); + return 1; + } + gzprintf (output, "=%s", *buffer_p); + } + notmuch_message_properties_destroy (list); + + if (! first) + gzprintf (output, "\n", *buffer_p); + + return 0; } static int @@ -159,7 +224,7 @@ database_dump_file (notmuch_database_t *notmuch, gzFile output, return EXIT_FAILURE; } - if (! (include & DUMP_INCLUDE_TAGS)) + if (! (include & (DUMP_INCLUDE_TAGS | DUMP_INCLUDE_PROPERTIES))) return EXIT_SUCCESS; if (! query_str) @@ -189,6 +254,11 @@ database_dump_file (notmuch_database_t *notmuch, gzFile output, &buffer, &buffer_size)) return EXIT_FAILURE; + if ((include & DUMP_INCLUDE_PROPERTIES) && + dump_properties_message (notmuch, message, output, + &buffer, &buffer_size)) + return EXIT_FAILURE; + notmuch_message_destroy (message); } @@ -312,6 +382,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[]) { 0, 0 } } }, { NOTMUCH_OPT_KEYWORD_FLAGS, &include, "include", 'I', (notmuch_keyword_t []){ { "config", DUMP_INCLUDE_CONFIG }, + { "properties", DUMP_INCLUDE_PROPERTIES }, { "tags", DUMP_INCLUDE_TAGS} } }, { NOTMUCH_OPT_STRING, &output_file_name, "output", 'o', 0 }, { NOTMUCH_OPT_BOOLEAN, &gzip_output, "gzip", 'z', 0 }, @@ -326,7 +397,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_process_shared_options (argv[0]); if (include == 0) - include = DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS; + include = DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS | DUMP_INCLUDE_PROPERTIES; if (opt_index < argc) { query_str = query_string_from_args (notmuch, argc - opt_index, argv + opt_index); diff --git a/notmuch-new.c b/notmuch-new.c index 799fec20..c55dea7b 100644 --- a/notmuch-new.c +++ b/notmuch-new.c @@ -1042,7 +1042,7 @@ notmuch_new_command (notmuch_config_t *config, int argc, char *argv[]) } if (notmuch_database_dump (notmuch, backup_name, "", - DUMP_FORMAT_BATCH_TAG, DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS, TRUE)) { + DUMP_FORMAT_BATCH_TAG, DUMP_INCLUDE_DEFAULT, TRUE)) { fprintf (stderr, "Backup failed. Aborting upgrade."); return EXIT_FAILURE; } diff --git a/test/T610-message-property.sh b/test/T610-message-property.sh index b5ddb7a4..a9b76de1 100755 --- a/test/T610-message-property.sh +++ b/test/T610-message-property.sh @@ -89,6 +89,17 @@ testkey2 = NULL EOF test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "notmuch_message_remove_all_properties" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +EXPECT0(notmuch_message_remove_all_properties (message, NULL)); +print_properties (message, "", FALSE); +EOF +cat <<'EOF' >EXPECTED +== stdout == +== stderr == +EOF +test_expect_equal_file EXPECTED OUTPUT + test_begin_subtest "notmuch_message_get_properties: empty list" cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} { @@ -188,4 +199,14 @@ cat <<'EOF' >EXPECTED EOF test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "dump message properties" +cat < PROPERTIES +#= 4EFC743A.3060609@april.org fancy%20key%20with%20%c3%a1cc%c3%a8nts=import%20value%20with%20= testkey1=alice testkey1=bob testkey1=testvalue1 testkey1=testvalue2 testkey3=alice3 testkey3=bob3 testkey3=testvalue3 +EOF +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +EXPECT0(notmuch_message_add_property (message, "fancy key with áccènts", "import value with =")); +EOF +notmuch dump | grep '^#=' > OUTPUT +test_expect_equal_file PROPERTIES OUTPUT + test_done From 651da30c0941081cf730930fc1a7cac34954ca0e Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 12 Jun 2016 22:05:55 -0300 Subject: [PATCH 134/179] cli: optionally restore message properties from dump file This somewhat mimics the config line parsing, except there can be arbitrarily many key value pairs, so one more level of looping is required. --- doc/man1/notmuch-restore.rst | 13 ++++-- notmuch-restore.c | 85 +++++++++++++++++++++++++++++++++-- test/T610-message-property.sh | 28 ++++++++++++ 3 files changed, 120 insertions(+), 6 deletions(-) diff --git a/doc/man1/notmuch-restore.rst b/doc/man1/notmuch-restore.rst index 706f8912..c681fa2d 100644 --- a/doc/man1/notmuch-restore.rst +++ b/doc/man1/notmuch-restore.rst @@ -50,7 +50,7 @@ Supported options for **restore** include format, this heuristic, based the fact that batch-tag format contains no parentheses, should be accurate. - ``--include=(config|tags)`` + ``--include=(config|properties|tags)`` Control what kind of metadata is restored. @@ -60,13 +60,20 @@ Supported options for **restore** include with "#@ ", followed by a space separated key-value pair. Both key and value are hex encoded if needed. + **properties** + + Output per-message (key,value) metadata. Each line starts + with "#= ", followed by a message id, and a space separated + list of key=value pairs. pair. Ids, keys and values are + hex encoded if needed. + **tags** Output per-message metadata, namely tags. See *format* above for more details. - The default is to restore both tags and configuration - information + The default is to restore all available types of data. The + option can be specified multiple times to select some subset. ``--input=``\ Read input from given file instead of stdin. diff --git a/notmuch-restore.c b/notmuch-restore.c index 371237c5..d6429efb 100644 --- a/notmuch-restore.c +++ b/notmuch-restore.c @@ -57,6 +57,72 @@ process_config_line (notmuch_database_t *notmuch, const char* line) return ret; } +static int +process_properties_line (notmuch_database_t *notmuch, const char* line) + +{ + const char *id_p, *tok; + size_t id_len = 0, tok_len = 0; + char *id; + + notmuch_message_t *message = NULL; + const char *delim = " \t\n"; + int ret = EXIT_FAILURE; + + void *local = talloc_new (NULL); + + id_p = strtok_len_c (line, delim, &id_len); + id = talloc_strndup (local, id_p, id_len); + if (hex_decode_inplace (id) != HEX_SUCCESS) { + fprintf (stderr, "hex decoding failure on line %s\n", line); + goto DONE; + } + + if (print_status_database ("notmuch restore", notmuch, + notmuch_database_find_message (notmuch, id, &message))) + goto DONE; + + if (print_status_database ("notmuch restore", notmuch, + notmuch_message_remove_all_properties (message, NULL))) + goto DONE; + + tok = id_p + id_len; + + while ((tok = strtok_len_c (tok + tok_len, delim, &tok_len)) != NULL) { + char *key, *value; + size_t off = strcspn (tok, "="); + if (off > tok_len) { + fprintf (stderr, "unparsable token %s\n", tok); + goto DONE; + } + + key = talloc_strndup (local, tok, off); + value = talloc_strndup (local, tok + off + 1, tok_len - off - 1); + + if (hex_decode_inplace (key) != HEX_SUCCESS) { + fprintf (stderr, "hex decoding failure on key %s\n", key); + goto DONE; + } + + if (hex_decode_inplace (value) != HEX_SUCCESS) { + fprintf (stderr, "hex decoding failure on value %s\n", value); + goto DONE; + } + + if (print_status_database ("notmuch restore", notmuch, + notmuch_message_add_property (message, key, value))) + goto DONE; + + } + + ret = EXIT_SUCCESS; + + DONE: + talloc_free (local); + return ret; +} + + static regex_t regex; /* Non-zero return indicates an error in retrieving the message, @@ -188,6 +254,7 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[]) { 0, 0 } } }, { NOTMUCH_OPT_KEYWORD_FLAGS, &include, "include", 'I', (notmuch_keyword_t []){ { "config", DUMP_INCLUDE_CONFIG }, + { "properties", DUMP_INCLUDE_PROPERTIES }, { "tags", DUMP_INCLUDE_TAGS} } }, { NOTMUCH_OPT_STRING, &input_file_name, "input", 'i', 0 }, @@ -206,7 +273,7 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[]) notmuch_exit_if_unmatched_db_uuid (notmuch); if (include == 0) { - include = DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS; + include = DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_PROPERTIES | DUMP_INCLUDE_TAGS; } name_for_error = input_file_name ? input_file_name : "stdin"; @@ -273,13 +340,18 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[]) if (ret) goto DONE; } + if ((include & DUMP_INCLUDE_PROPERTIES) && line_len >= 2 && line[0] == '#' && line[1] == '=') { + ret = process_properties_line (notmuch, line + 2); + if (ret) + goto DONE; + } } while ((line_len == 0) || (line[0] == '#') || /* the cast is safe because we checked about for line_len < 0 */ (strspn (line, " \t\n") == (unsigned)line_len)); - if (! (include & DUMP_INCLUDE_TAGS)) { + if (! ((include & DUMP_INCLUDE_TAGS) || (include & DUMP_INCLUDE_PROPERTIES))) { ret = EXIT_SUCCESS; goto DONE; } @@ -306,6 +378,13 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[]) talloc_free (line_ctx); line_ctx = talloc_new (config); + + if ((include & DUMP_INCLUDE_PROPERTIES) && line_len >= 2 && line[0] == '#' && line[1] == '=') { + ret = process_properties_line (notmuch, line + 2); + if (ret) + goto DONE; + } + if (input_format == DUMP_FORMAT_SUP) { ret = parse_sup_line (line_ctx, line, &query_string, tag_ops); } else { @@ -344,7 +423,7 @@ notmuch_restore_command (notmuch_config_t *config, int argc, char *argv[]) break; } while (! (ret = gz_getline (line_ctx, &line, &line_len, input))); - + /* EOF is normal loop termination condition, UTIL_SUCCESS is * impossible here */ diff --git a/test/T610-message-property.sh b/test/T610-message-property.sh index a9b76de1..c92b99ba 100755 --- a/test/T610-message-property.sh +++ b/test/T610-message-property.sh @@ -209,4 +209,32 @@ EOF notmuch dump | grep '^#=' > OUTPUT test_expect_equal_file PROPERTIES OUTPUT + +test_begin_subtest "restore missing message property (single line)" +notmuch dump | grep '^#=' > BEFORE1 +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +EXPECT0(notmuch_message_remove_property (message, "testkey1", "bob")); +EOF +notmuch restore < BEFORE1 +notmuch dump | grep '^#=' > OUTPUT +test_expect_equal_file PROPERTIES OUTPUT + + +test_begin_subtest "restore missing message property (full dump)" +notmuch dump > BEFORE2 +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +EXPECT0(notmuch_message_remove_property (message, "testkey1", "bob")); +EOF +notmuch restore < BEFORE2 +notmuch dump | grep '^#=' > OUTPUT +test_expect_equal_file PROPERTIES OUTPUT + +test_begin_subtest "restore clear extra message property" +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} +EXPECT0(notmuch_message_add_property (message, "testkey1", "charles")); +EOF +notmuch restore < BEFORE2 +notmuch dump | grep '^#=' > OUTPUT +test_expect_equal_file PROPERTIES OUTPUT + test_done From 693ca8d8a80438ce1e361e7e6d22288f52a11c55 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 8 Jul 2016 11:15:36 +0200 Subject: [PATCH 135/179] add property: query prefix to search for specific properties We want to be able to query the properties directly, like: notmuch count property:foo=bar which should return a count of messages where the property with key "foo" has value equal to "bar". --- doc/man7/notmuch-search-terms.rst | 9 ++++++++- lib/database.cc | 5 ++++- test/T610-message-property.sh | 11 +++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/doc/man7/notmuch-search-terms.rst b/doc/man7/notmuch-search-terms.rst index 075f88c8..de93d733 100644 --- a/doc/man7/notmuch-search-terms.rst +++ b/doc/man7/notmuch-search-terms.rst @@ -58,6 +58,8 @@ indicate user-supplied values): - query: +- property:= + The **from:** prefix is used to match the name or address of the sender of an email message. @@ -139,6 +141,11 @@ queries added with **notmuch-config(1)**. Named queries are only available if notmuch is built with **Xapian Field Processors** (see below). +The **property:** prefix searches for messages with a particular += property pair. Properties are used internally by notmuch +(and extensions) to add metadata to messages. A given key can be +present on a given message with several different values. + Operators --------- @@ -217,7 +224,7 @@ exact matches like "tag:inbox" or **probabilistic**, supporting a more flexible Boolean - **tag:**, **id:**, **thread:**, **folder:**, **path:** + **tag:**, **id:**, **thread:**, **folder:**, **path:**, **property:** Probabilistic **from:**, **to:**, **subject:**, **attachment:**, **mimetype:** diff --git a/lib/database.cc b/lib/database.cc index 33e22d86..392e8b28 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -97,6 +97,9 @@ typedef struct { * STRING is the name of a file within that * directory for this mail message. * + * property: Has a property with key=value + * FIXME: if no = is present, should match on any value + * * A mail document also has four values: * * TIMESTAMP: The time_t value corresponding to the message's @@ -251,7 +254,6 @@ static prefix_t BOOLEAN_PREFIX_INTERNAL[] = { { "directory", "XDIRECTORY" }, { "file-direntry", "XFDIRENTRY" }, { "directory-direntry", "XDDIRENTRY" }, - { "property", "XPROPERTY" }, }; static prefix_t BOOLEAN_PREFIX_EXTERNAL[] = { @@ -260,6 +262,7 @@ static prefix_t BOOLEAN_PREFIX_EXTERNAL[] = { { "is", "K" }, { "id", "Q" }, { "path", "P" }, + { "property", "XPROPERTY" }, /* * Unconditionally add ':' to reduce potential ambiguity with * overlapping prefixes and/or terms that start with capital diff --git a/test/T610-message-property.sh b/test/T610-message-property.sh index c92b99ba..65ff19dc 100755 --- a/test/T610-message-property.sh +++ b/test/T610-message-property.sh @@ -237,4 +237,15 @@ notmuch restore < BEFORE2 notmuch dump | grep '^#=' > OUTPUT test_expect_equal_file PROPERTIES OUTPUT +test_begin_subtest "test 'property:' queries: empty" +notmuch search property:testkey1=charles > OUTPUT +test_expect_equal_file /dev/null OUTPUT + +test_begin_subtest "test 'property:' queries: single message" +notmuch search --output=messages property:testkey1=alice > OUTPUT +cat <EXPECTED +id:4EFC743A.3060609@april.org +EOF +test_expect_equal_file EXPECTED OUTPUT + test_done From 50a1032f870444522d90ef9d23726e42517fedbc Mon Sep 17 00:00:00 2001 From: David Bremner Date: Wed, 21 Sep 2016 20:09:14 -0300 Subject: [PATCH 136/179] build system: choose gnu99 as a C standard Apparently pre 5.1 gcc defaulted to gnu89, but we decided it was ok to use some c99 features. '-std=c99' by itself is not enough for notmuch to compile. '-std=gnu99' seems to work with clang and gcc, so I'm not convinced configuration support is needed. --- Makefile.local | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile.local b/Makefile.local index 5587cd2c..d1b0585f 100644 --- a/Makefile.local +++ b/Makefile.local @@ -44,7 +44,8 @@ GPG_FILE=$(SHA1_FILE).asc PV_FILE=bindings/python/notmuch/version.py # Smash together user's values with our extra values -FINAL_CFLAGS = -DNOTMUCH_VERSION=$(VERSION) $(CPPFLAGS) $(CFLAGS) $(WARN_CFLAGS) $(extra_cflags) $(CONFIGURE_CFLAGS) +STD_CFLAGS := -std=gnu99 +FINAL_CFLAGS = -DNOTMUCH_VERSION=$(VERSION) $(CPPFLAGS) $(STD_CFLAGS) $(CFLAGS) $(WARN_CFLAGS) $(extra_cflags) $(CONFIGURE_CFLAGS) FINAL_CXXFLAGS = $(CPPFLAGS) $(CXXFLAGS) $(WARN_CXXFLAGS) $(extra_cflags) $(extra_cxxflags) $(CONFIGURE_CXXFLAGS) FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Lutil -lutil -Llib -lnotmuch ifeq ($(LIBDIR_IN_LDCONFIG),0) From 1d4812fd616dbabb11eed0f3718fa7827d69355f Mon Sep 17 00:00:00 2001 From: David Bremner Date: Thu, 22 Sep 2016 08:11:42 -0300 Subject: [PATCH 137/179] test: fix printf format notmuch_status_t is an integer type, printing it as a string is a very bad idea. --- test/notmuch-test.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/notmuch-test.h b/test/notmuch-test.h index d39febbe..45d03d67 100644 --- a/test/notmuch-test.h +++ b/test/notmuch-test.h @@ -7,7 +7,7 @@ inline static void expect0(int line, notmuch_status_t ret) { if (ret) { - fprintf (stderr, "line %d: %s\n", line, ret); + fprintf (stderr, "line %d: %d\n", line, ret); exit (1); } } From 67873799ea049a3438ab464bd01cde8dbc3858d8 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sat, 17 Sep 2016 10:07:44 -0300 Subject: [PATCH 138/179] NEWS: news for Xapian 1.4 enabled features In fact some of these features are available in Xapian 1.3.x development releases, but these are not really widely packaged. In any case, the experts who are using development releases of Xapian can figure that out. --- NEWS | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/NEWS b/NEWS index c0b865df..f6ecbc76 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,42 @@ Notmuch 0.23 (UNRELEASED) ========================= +General (Xapian 1.4+) +--------------------- + +Compiling against Xapian 1.4 enables several new features. + +Support for single argument date: queries + + `date:` is equivalent to `date:..`. + +Support for blocking opens + + When opening a database notmuch by default will wait for another + process to release a write lock, rather than returning an error. + +Support for named queries + + Named queries (also known as 'saved searches') can be defined with a + `query:name` format. The expansion of these queries is stored in the + database and they can be used from any notmuch client. + +CLI +--- + +Support for compile time options + + A group of `built_with` keys is now supported for notmuch + config. Initial keys in this group are `compact`, `field_processor`, + and `retry_lock`. + +Dump/Restore support for configuration information + + Any configuration information stored in the database (initially just + named queries) is dumped and restored. Any new information in the + dump format is prefixed by '#' to allow existing scripts to ignore + it. + Emacs ----- From efc17bb5617708bc543ca585865ee45e7aeedd6e Mon Sep 17 00:00:00 2001 From: David Bremner Date: Fri, 23 Sep 2016 20:58:32 -0300 Subject: [PATCH 139/179] NEWS: fix markup of CONSTANTS_WITH_UNDERSCORES Alas, I did not notice when when I originally applied the NEWS patch --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index f6ecbc76..0fd6d724 100644 --- a/NEWS +++ b/NEWS @@ -62,7 +62,7 @@ Add support for `notmuch_threads_t` and `notmuch_thread_t` Fixed constant values so they are not all zero anymore. Previously, it was impossible to open writable database handles, - because DATABASE_MODE_READ_ONLY and DATABASE_MODE_READ_WRITE were + because `DATABASE_MODE_READ_ONLY` and `DATABASE_MODE_READ_WRITE` were both set to zero. The same issue occured with sort modes. From 5292e9b1ae60bbb66466f126463b3d6f0bf6a3b6 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Mon, 12 Sep 2016 23:24:43 -0300 Subject: [PATCH 140/179] test/emacs: add broken test for message replying to msg to self This is a strange corner case where the removing of the user's address from the To: header does the wrong thing. If we think it is worth (eventually) fixing, this test can serve as a reminder. --- test/T310-emacs.sh | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/T310-emacs.sh b/test/T310-emacs.sh index 21675b68..01385ae8 100755 --- a/test/T310-emacs.sh +++ b/test/T310-emacs.sh @@ -521,6 +521,30 @@ Notmuch Test Suite writes: EOF test_expect_equal_file OUTPUT EXPECTED +test_begin_subtest "Reply within emacs to message from self" +test_subtest_known_broken +add_message '[from]="test_suite@notmuchmail.org"' \ + '[to]="test_suite@notmuchmail.org"' +test_emacs "(let ((message-hidden-headers '())) + (notmuch-show \"id:${gen_msg_id}\") + (notmuch-show-reply) + (test-output))" +sed -i -e 's/^In-Reply-To: <.*>$/In-Reply-To: /' OUTPUT +sed -i -e 's/^References: <.*>$/References: /' OUTPUT +cat <EXPECTED +From: Notmuch Test Suite +To: test_suite@notmuchmail.org +Subject: Re: Reply within emacs to message from self +In-Reply-To: +Fcc: ${MAIL_DIR}/sent +References: +--text follows this line-- +test_suite@notmuchmail.org writes: + +> This is just a test message (#7) +EOF +test_expect_equal_file EXPECTED OUTPUT + test_begin_subtest "Quote MML tags in reply" message_id='test-emacs-mml-quoting@message.id' add_message [id]="$message_id" \ From 7b2d7d65126b11ce08079b7cf235e1073fb1c22b Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Mon, 19 Sep 2016 10:39:49 +0100 Subject: [PATCH 141/179] emacs: tree: make jump close message pane j is in the global notmuch keymap bound to notmuch jump. In tree-mode it makes sense to close the message pane first (otherwise the new search runs in the small top pane of tree-mode). --- emacs/notmuch-tree.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/emacs/notmuch-tree.el b/emacs/notmuch-tree.el index d864e6dd..6e5797e9 100644 --- a/emacs/notmuch-tree.el +++ b/emacs/notmuch-tree.el @@ -241,6 +241,8 @@ FUNC." (define-key map [remap notmuch-search] 'notmuch-tree-to-search) ;; Override because we want to close message pane first. (define-key map [remap notmuch-mua-new-mail] (notmuch-tree-close-message-pane-and #'notmuch-mua-new-mail)) + ;; Override because we want to close message pane first. + (define-key map [remap notmuch-jump-search] (notmuch-tree-close-message-pane-and #'notmuch-jump-search)) (define-key map "S" 'notmuch-search-from-tree-current-query) From 514a0a6a3bd74c03db6cd9b28edc0a29a8a2441e Mon Sep 17 00:00:00 2001 From: David Bremner Date: Fri, 23 Sep 2016 06:33:19 -0300 Subject: [PATCH 142/179] lib: add talloc reference from string map iterator to map This is needed so that when the map is modified during traversal, and thus unlinked by the database code, the map is not disposed of until the iterator is done with it. --- lib/string-map.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/string-map.c b/lib/string-map.c index 591ff6d5..0bb77e93 100644 --- a/lib/string-map.c +++ b/lib/string-map.c @@ -170,6 +170,9 @@ _notmuch_string_map_iterator_create (notmuch_string_map_t *map, const char *key, if (unlikely (iter == NULL)) return NULL; + if (unlikely (talloc_reference (iter, map) == NULL)) + return NULL; + iter->key = talloc_strdup (iter, key); iter->exact = exact; iter->current = bsearch_first (map->pairs, map->length, key, exact); From b57426a43549ac5434c0f793e184f1b0b955f7e0 Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Sat, 24 Sep 2016 15:43:27 +0300 Subject: [PATCH 143/179] test: test-lib.sh: use $BASH instead of ${SHELL-sh} to relaunch In case of the test script is to be relaunced under valgrind, or --tee is requested, use the $BASH shell variable to locate the command interpreter. The $SHELL variable is re-set by non-interactive shells so in case the shell uses some other shell (e.g. zsh) for interactive use these bash scripts continue to work. --- test/test-lib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test-lib.sh b/test/test-lib.sh index e2e26e6f..bda8a80a 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -39,7 +39,7 @@ done,*) *' --tee '*|*' --va'*) mkdir -p test-results BASE=test-results/$this_test - (GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1; + (GIT_TEST_TEE_STARTED=done "$BASH" "$0" "$@" 2>&1; echo $? > $BASE.exit) | tee $BASE.out test "$(cat $BASE.exit)" = 0 exit From 51d27e08555ac3187bd9d5649a0074ba6a9aabce Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Sun, 18 Sep 2016 11:57:32 +0100 Subject: [PATCH 144/179] emacs: tag deleted face bugfix Commit d25d33ff cleaned up some of the tag face code. However, for the face notmuch-tag-deleted it used the test ((class color) (supports :strike-through)) to decide whether to use red strikethrough or inverse-video (emacs in a terminal typically doesn't support red strikethrough, but in X it does). However, it seems that test often returns true even though red strikethrough is not supported. This breaks the tag update code -- the wrong thing is displayed to the user. Thus we make the test explicitly more specific, changing the test to ((class color) (supports :strike-through "red")) --- emacs/notmuch-tag.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emacs/notmuch-tag.el b/emacs/notmuch-tag.el index ec3c964d..6c8b6a75 100644 --- a/emacs/notmuch-tag.el +++ b/emacs/notmuch-tag.el @@ -105,7 +105,7 @@ with images." :type 'notmuch-tag-format-type) (defface notmuch-tag-deleted - '((((class color) (supports :strike-through)) :strike-through "red") + '((((class color) (supports :strike-through "red")) :strike-through "red") (t :inverse-video t)) "Face used to display deleted tags. From 76a1f46a44a324b6abf81b8bf480b195a5744925 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Mon, 26 Sep 2016 07:22:21 -0300 Subject: [PATCH 145/179] version: bump to 0.23~rc0 --- bindings/python/notmuch/version.py | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/python/notmuch/version.py b/bindings/python/notmuch/version.py index 6d2b25fd..2977e573 100644 --- a/bindings/python/notmuch/version.py +++ b/bindings/python/notmuch/version.py @@ -1,3 +1,3 @@ # this file should be kept in sync with ../../../version -__VERSION__ = '0.22.2' +__VERSION__ = '0.23~rc0' SOVERSION = '4' diff --git a/version b/version index faa5fb26..aa335147 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.22.2 +0.23~rc0 From 18e720314fce5836f89b72c469e7a3d596f9ae7e Mon Sep 17 00:00:00 2001 From: David Bremner Date: Mon, 26 Sep 2016 07:30:15 -0300 Subject: [PATCH 146/179] debian: changelog stanza for 0.23~rc0-1 --- debian/changelog | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/debian/changelog b/debian/changelog index fb82015d..41fbbd68 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,13 @@ +notmuch (0.23~rc0-1) experimental; urgency=medium + + * New upstream release candidate + * Bug fix: "Calls to notmuch_directory_get_mtime() don't return + the recently set mtime", thanks to Lars Luthman (Closes: #826881). + * Bug fix: "Please document compact command", thanks to Olivier + Berger (Closes: #825884). + + -- David Bremner Mon, 26 Sep 2016 07:28:06 -0300 + notmuch (0.22.2-1) unstable; urgency=medium * Fix test suite compatibility with GnuPG 2.1.15. Bug fix: "FTBFS: From 114d1cd0d02db239cf1a69b61243bc116f7907f8 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Mon, 26 Sep 2016 07:39:25 -0300 Subject: [PATCH 147/179] debian: update symbols for 0.23 New sub-APIs: config, built_with, message_property. A new exception used internally. Mark the exception as optional, as it only shows up when built against Xapian supporting FieldProcessors --- debian/libnotmuch4.symbols | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/debian/libnotmuch4.symbols b/debian/libnotmuch4.symbols index 7dd54237..c8a94ba6 100644 --- a/debian/libnotmuch4.symbols +++ b/debian/libnotmuch4.symbols @@ -1,4 +1,10 @@ libnotmuch.so.4 libnotmuch4 #MINVER# + notmuch_built_with@Base 0.23~rc0 + notmuch_config_list_destroy@Base 0.23~rc0 + notmuch_config_list_key@Base 0.23~rc0 + notmuch_config_list_move_to_next@Base 0.23~rc0 + notmuch_config_list_valid@Base 0.23~rc0 + notmuch_config_list_value@Base 0.23~rc0 notmuch_database_add_message@Base 0.3 notmuch_database_begin_atomic@Base 0.9~rc1 notmuch_database_close@Base 0.13~rc1 @@ -10,6 +16,8 @@ libnotmuch.so.4 libnotmuch4 #MINVER# notmuch_database_find_message@Base 0.9~rc2 notmuch_database_find_message_by_filename@Base 0.9~rc2 notmuch_database_get_all_tags@Base 0.3 + notmuch_database_get_config@Base 0.23~rc0 + notmuch_database_get_config_list@Base 0.23~rc0 notmuch_database_get_directory@Base 0.3 notmuch_database_get_path@Base 0.3 notmuch_database_get_revision@Base 0.21~rc1 @@ -18,6 +26,7 @@ libnotmuch.so.4 libnotmuch4 #MINVER# notmuch_database_open@Base 0.3 notmuch_database_open_verbose@Base 0.20~rc1 notmuch_database_remove_message@Base 0.3 + notmuch_database_set_config@Base 0.23~rc0 notmuch_database_status_string@Base 0.20~rc1 notmuch_database_upgrade@Base 0.3 notmuch_directory_delete@Base 0.21~rc1 @@ -30,6 +39,7 @@ libnotmuch.so.4 libnotmuch4 #MINVER# notmuch_filenames_get@Base 0.3 notmuch_filenames_move_to_next@Base 0.3 notmuch_filenames_valid@Base 0.3 + notmuch_message_add_property@Base 0.23~rc0 notmuch_message_add_tag@Base 0.3 notmuch_message_destroy@Base 0.3 notmuch_message_freeze@Base 0.3 @@ -39,11 +49,20 @@ libnotmuch.so.4 libnotmuch4 #MINVER# notmuch_message_get_flag@Base 0.3 notmuch_message_get_header@Base 0.3 notmuch_message_get_message_id@Base 0.3 + notmuch_message_get_properties@Base 0.23~rc0 + notmuch_message_get_property@Base 0.23~rc0 notmuch_message_get_replies@Base 0.3 notmuch_message_get_tags@Base 0.3 notmuch_message_get_thread_id@Base 0.3 notmuch_message_maildir_flags_to_tags@Base 0.5 + notmuch_message_properties_destroy@Base 0.23~rc0 + notmuch_message_properties_key@Base 0.23~rc0 + notmuch_message_properties_move_to_next@Base 0.23~rc0 + notmuch_message_properties_valid@Base 0.23~rc0 + notmuch_message_properties_value@Base 0.23~rc0 + notmuch_message_remove_all_properties@Base 0.23~rc0 notmuch_message_remove_all_tags@Base 0.3 + notmuch_message_remove_property@Base 0.23~rc0 notmuch_message_remove_tag@Base 0.3 notmuch_message_set_flag@Base 0.3 notmuch_message_tags_to_maildir_flags@Base 0.5 @@ -94,8 +113,10 @@ libnotmuch.so.4 libnotmuch4 #MINVER# (c++)"typeinfo for Xapian::DocNotFoundError@Base" 0.6.1 (c++)"typeinfo for Xapian::InvalidArgumentError@Base" 0.6.1 (c++)"typeinfo for Xapian::Error@Base" 0.6.1 + (c++|optional=present with Xapian 1.4)"typeinfo for Xapian::QueryParserError@Base" 0.23~rc0 (c++)"typeinfo name for Xapian::LogicError@Base" 0.6.1 (c++)"typeinfo name for Xapian::RuntimeError@Base" 0.6.1 (c++)"typeinfo name for Xapian::DocNotFoundError@Base" 0.6.1 (c++)"typeinfo name for Xapian::InvalidArgumentError@Base" 0.6.1 (c++)"typeinfo name for Xapian::Error@Base" 0.6.1 + (c++|optional=present with Xapian 1.4)"typeinfo name for Xapian::QueryParserError@Base" 0.23~rc0 From 9966720453c72286b5f36a56323244d70202cb26 Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Sun, 25 Sep 2016 08:18:10 +0100 Subject: [PATCH 148/179] emacs: tree: make b bounce a message and backspace scroll message pane up This tweaks the keybindings in tree-mode. It make b do bounce/resend matching show-mode. Since b was already bound to scroll message pane back, we now use backspace for that. This means space/backspace scroll the message pane forwards/backwards, and page-up/page-down scrolls the tree pane forwards/backwards. --- emacs/notmuch-tree.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/emacs/notmuch-tree.el b/emacs/notmuch-tree.el index 6e5797e9..658c4f90 100644 --- a/emacs/notmuch-tree.el +++ b/emacs/notmuch-tree.el @@ -251,6 +251,7 @@ FUNC." (define-key map "w" 'notmuch-show-save-attachments) (define-key map "v" 'notmuch-show-view-all-mime-parts) (define-key map "c" 'notmuch-show-stash-map) + (define-key map "b" 'notmuch-show-resend-message) ;; these apply to the message pane (define-key map (kbd "M-TAB") (notmuch-tree-to-message-pane #'notmuch-show-previous-button)) @@ -282,7 +283,7 @@ FUNC." (define-key map "+" 'notmuch-tree-add-tag) (define-key map "*" 'notmuch-tree-tag-thread) (define-key map " " 'notmuch-tree-scroll-or-next) - (define-key map "b" 'notmuch-tree-scroll-message-window-back) + (define-key map (kbd "DEL") 'notmuch-tree-scroll-message-window-back) map)) (fset 'notmuch-tree-mode-map notmuch-tree-mode-map) From 4ebbdd1a2150db0e3d0223cac55b784653e8d2eb Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 25 Sep 2016 08:12:40 -0300 Subject: [PATCH 149/179] NEWS: document mtime bugfix --- NEWS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/NEWS b/NEWS index 0fd6d724..2f1649f3 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,13 @@ Support for named queries `query:name` format. The expansion of these queries is stored in the database and they can be used from any notmuch client. +Library +------- + +Bug fix for `notmuch_directory_set_mtime` + + Update cached mtime to match on-disk mtime. + CLI --- From 853ee0b909f59c61bfc5773d0f64d357f4a59c94 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 25 Sep 2016 08:22:50 -0300 Subject: [PATCH 150/179] NEWS: note dropping of User-Agent header --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 2f1649f3..8ed8d835 100644 --- a/NEWS +++ b/NEWS @@ -54,6 +54,8 @@ Face customization is easier `notmuch-search-flagged-face` and `notmuch-search-unread-face` are now used by default. Customize `notmuch-faces` to modify them. +Omit User-Agent header by default when sending mail + Ruby Bindings ------------- From 71e312b97864fddadaa9487bae8bd8c55dff7ab4 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 25 Sep 2016 08:30:49 -0300 Subject: [PATCH 151/179] NEWS: note notmuch-reply refactor --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 8ed8d835..751b9196 100644 --- a/NEWS +++ b/NEWS @@ -44,6 +44,8 @@ Dump/Restore support for configuration information dump format is prefixed by '#' to allow existing scripts to ignore it. +Refactor notmuch-reply, make handling of duplicate headers consistent + Emacs ----- From 64cfd47e4be1851a419baf0f4b2b21ecfdc69e86 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 25 Sep 2016 08:33:26 -0300 Subject: [PATCH 152/179] NEWS: fix headline for go bindings NEWS --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 751b9196..3a5863a6 100644 --- a/NEWS +++ b/NEWS @@ -70,7 +70,7 @@ Go bindings moved to contrib Add support for `notmuch_threads_t` and `notmuch_thread_t` -Fixed constant values so they are not all zero anymore. +Fixed constant values so they are not all zero anymore Previously, it was impossible to open writable database handles, because `DATABASE_MODE_READ_ONLY` and `DATABASE_MODE_READ_WRITE` were From 35fc6d3c183f7d688db56000c2a703dbe978f218 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 25 Sep 2016 08:36:52 -0300 Subject: [PATCH 153/179] NEWS: mention message properties API, dump/restore --- NEWS | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 3a5863a6..5590bb81 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,13 @@ Support for named queries Library ------- +Message property API + + libnotmuch now supports the attachment of arbitrary key-value pairs + to messages. These can be used by various tools to manage their + private data without polluting the user tag space. They also support + iteration of values with the same key or same key prefix. + Bug fix for `notmuch_directory_set_mtime` Update cached mtime to match on-disk mtime. @@ -37,14 +44,13 @@ Support for compile time options config. Initial keys in this group are `compact`, `field_processor`, and `retry_lock`. -Dump/Restore support for configuration information +Dump/Restore support for configuration information and properties Any configuration information stored in the database (initially just - named queries) is dumped and restored. Any new information in the - dump format is prefixed by '#' to allow existing scripts to ignore - it. - -Refactor notmuch-reply, make handling of duplicate headers consistent + named queries) is dumped and restored. Similarly any properties + attached to messages are also dumped and restored. Any new + information in the dump format is prefixed by '#' to allow existing + scripts to ignore it. Emacs ----- From b844c3c5068470cccd6522a44a9a6d892f440426 Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Tue, 27 Sep 2016 20:24:41 +0300 Subject: [PATCH 154/179] NEWS: emacs notmuch-show-resend-message --- NEWS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/NEWS b/NEWS index 5590bb81..04ab32d2 100644 --- a/NEWS +++ b/NEWS @@ -55,6 +55,14 @@ Dump/Restore support for configuration information and properties Emacs ----- +Resend messages + + The function `notmuch-show-resend-message` (bound to `b` in show + and tree modes) will (attempt to) send current message to new + recipients. The headers of the message won't be altered (e.g. `To:` + may point to yourself). New `Resent-To:`, `Resent-From:` and so on + will be added instead. + Face customization is easier New faces `notmuch-tag-unread`, `notmuch-tag-flagged`, From 1c3a8e0898be614b5183f0ae626386f7746e7e07 Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Tue, 27 Sep 2016 18:06:52 +0300 Subject: [PATCH 155/179] lib/database.cc: fix misleading indentation Found by gcc 6.1.1 -Wmisleading-indentation option (set by -Wall). --- lib/database.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/database.cc b/lib/database.cc index 392e8b28..4bfae019 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -1685,8 +1685,8 @@ notmuch_database_begin_atomic (notmuch_database_t *notmuch) notmuch->atomic_nesting > 0) goto DONE; - if (notmuch_database_needs_upgrade(notmuch)) - return NOTMUCH_STATUS_UPGRADE_REQUIRED; + if (notmuch_database_needs_upgrade (notmuch)) + return NOTMUCH_STATUS_UPGRADE_REQUIRED; try { (static_cast (notmuch->xapian_db))->begin_transaction (false); From cdd7b0796b068d0be1bd104afe320b9e81e98f5e Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Wed, 28 Sep 2016 13:33:54 +0100 Subject: [PATCH 156/179] NEWS: add news for fcc insert and address completion changes --- NEWS | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/NEWS b/NEWS index 04ab32d2..805a0347 100644 --- a/NEWS +++ b/NEWS @@ -55,6 +55,43 @@ Dump/Restore support for configuration information and properties Emacs ----- +Make notmuch-message-mode use insert for fcc + + Notmuch-message-mode now defaults to using notmuch insert for + fcc. The old file based fcc behaviour can be restored by setting the + defcustom `notmuch-maildir-use-notmuch-insert` to nil. + + When using notmuch insert, `notmuch-fcc-dirs` must be a subdirectory + of the mailstore (absolute paths are not permitted) followed by any + tag changes to be applied to the inserted message. The tag changes + are applied after the default tagging for new messages. For example + setting the header to "sentmail -inbox +sent" would insert the + message in the subdirectory sentmail of the mailstore, add the tag + "sent", and not add the (normally added) "inbox" tag. + + Finally, if the insert fails (e.g. if the database is locked) the + user is presented with the option to retry, ignore, or edit the + header. + +Make internal address completion customizable + + There is a new defcustom `notmuch-address-internal-completion` which + controls how the internal completion works: it allows the user to + choose whether to match on messages the user sent, or the user + received, and to filter the messages used for the match, for example + by date. + +Allow internal address completion on an individual basis + + There is a new function `notmuch-address-toggle-internal-completion` + (by default it has no keybinding) which allows users who normally + use an external completion command to use the builtin internal + completion for the current buffer. + + Alternatively, if the user has company-mode enabled, then the user + can use company mode commands such as `company-complete` to + activate the builtin completion for an individual completion. + Resend messages The function `notmuch-show-resend-message` (bound to `b` in show From 4266e76eed0e0b2ab8a720b60f8b3cc515fe0aac Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Wed, 28 Sep 2016 11:03:46 +0100 Subject: [PATCH 157/179] emacs: document notmuch-fcc-dirs This updates the docstring for the variable notmuch-fcc-dirs to match the new insert code. --- emacs/notmuch-maildir-fcc.el | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/emacs/notmuch-maildir-fcc.el b/emacs/notmuch-maildir-fcc.el index 1218c01e..3587d045 100644 --- a/emacs/notmuch-maildir-fcc.el +++ b/emacs/notmuch-maildir-fcc.el @@ -30,14 +30,14 @@ (defvar notmuch-maildir-fcc-count 0) (defcustom notmuch-fcc-dirs "sent" - "Determines the maildir directory in which to save outgoing mail. + "Determines the Fcc Header which says where to save outgoing mail. Three types of values are permitted: - nil: no Fcc header is added, -- a string: the value of `notmuch-fcc-dirs' is the name of the - folder to use, +- a string: the value of `notmuch-fcc-dirs' is the Fcc header to + be used. - a list: the folder is chosen based on the From address of the current message using a list of regular expressions and @@ -50,12 +50,20 @@ Three types of values are permitted: If none of the regular expressions match the From address, no Fcc header will be added. -In all cases, a relative FCC directory will be understood to -specify a directory within the notmuch mail store, (as set by -the database.path option in the notmuch configuration file). +If `notmuch-maildir-use-notmuch-insert' is set (the default) then +the header should be of the form \"folder +tag1 -tag2\" where +folder is the folder (relative to the notmuch mailstore) to store +the message in, and tag1 and tag2 are tag changes to apply to the +stored message. -You will be prompted to create the directory if it does not exist -yet when sending a mail." +If `notmuch-maildir-use-notmuch-insert' is nil then the Fcc +header should be the directory where the message should be +saved. A relative directory will be understood to specify a +directory within the notmuch mail store, (as set by the +database.path option in the notmuch configuration file). + +In all cases you will be prompted to create the folder or +directory if it does not exist yet when sending a mail." :type '(choice (const :tag "No FCC header" nil) From 0b138c268641080c766801b4d74db3b693ca6eaa Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Thu, 29 Sep 2016 14:44:04 +0100 Subject: [PATCH 158/179] emacs: fcc: say we are doing Fcc Since doing the Fcc with notmuch insert could be slow (if the indexing takes some time) add a message saying we are doing it. --- emacs/notmuch-maildir-fcc.el | 1 + 1 file changed, 1 insertion(+) diff --git a/emacs/notmuch-maildir-fcc.el b/emacs/notmuch-maildir-fcc.el index 3587d045..95e56503 100644 --- a/emacs/notmuch-maildir-fcc.el +++ b/emacs/notmuch-maildir-fcc.el @@ -203,6 +203,7 @@ This is a rearranged version of message mode's message-do-fcc." If `notmuch-maildir-use-notmuch-insert` is set then store the message using notmuch insert. Otherwise store the message using normal fcc." + (message "Doing Fcc...") (if notmuch-maildir-use-notmuch-insert (notmuch-maildir-fcc-with-notmuch-insert fcc-header) (notmuch-maildir-fcc-file-fcc fcc-header))) From fde846cf7dea2ad67418f3f3c9311213b766bfec Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Thu, 29 Sep 2016 16:12:49 +0300 Subject: [PATCH 159/179] configure: set platform variables also when `uname` is unrecognized Since commit 124a67e96ecab5495c0f17b6875d53dfd67ff137: configure: add set -u all variables must be set before their expansion are attempted. These 2 variables: "platform" and "linker_resolves_library_dependencies" were not given value in the final 'else' branch when platform check failed due to unrecognized kernel name (output of `uname`). Now those two are given reasonable non-empty values. --- configure | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure b/configure index fa4c5b5e..cc6746c1 100755 --- a/configure +++ b/configure @@ -650,6 +650,8 @@ elif [ $uname = "Linux" ] || [ $uname = "GNU" ] ; then fi else printf "Unknown.\n" + platform="$uname" + linker_resolves_library_dependencies=0 cat < Date: Fri, 30 Sep 2016 07:18:04 -0300 Subject: [PATCH 160/179] version: update to 0.23~rc1 --- bindings/python/notmuch/version.py | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/python/notmuch/version.py b/bindings/python/notmuch/version.py index 2977e573..3e54cc24 100644 --- a/bindings/python/notmuch/version.py +++ b/bindings/python/notmuch/version.py @@ -1,3 +1,3 @@ # this file should be kept in sync with ../../../version -__VERSION__ = '0.23~rc0' +__VERSION__ = '0.23~rc1' SOVERSION = '4' diff --git a/version b/version index aa335147..866fd80c 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.23~rc0 +0.23~rc1 From 4241b4d4e6bec9d4036f39e1a6685466066f0cec Mon Sep 17 00:00:00 2001 From: David Bremner Date: Fri, 30 Sep 2016 07:21:09 -0300 Subject: [PATCH 161/179] debian: changelog stanza for 0.23~rc1-1 --- debian/changelog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/debian/changelog b/debian/changelog index 41fbbd68..741bdd36 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +notmuch (0.23~rc1-1) experimental; urgency=medium + + * New upstream release candidate + * Make configure more robust on "unknown" platforms. Fixes FTBFS on + kfreebsd. + + -- David Bremner Fri, 30 Sep 2016 07:19:26 -0300 + notmuch (0.23~rc0-1) experimental; urgency=medium * New upstream release candidate From c2e74662bbbbf9be1fde0ae416dc2664ba3da3cd Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sat, 1 Oct 2016 22:11:56 -0300 Subject: [PATCH 162/179] lib: bump minor version to mark added symbols This should not change the SONAME, and therefore won't change the dynamic linking behaviour, but it may help some users debug missing symbols in case their libnotmuch is too old. --- lib/notmuch.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/notmuch.h b/lib/notmuch.h index e03a05dd..251c29b5 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -56,7 +56,7 @@ NOTMUCH_BEGIN_DECLS * version in Makefile.local. */ #define LIBNOTMUCH_MAJOR_VERSION 4 -#define LIBNOTMUCH_MINOR_VERSION 3 +#define LIBNOTMUCH_MINOR_VERSION 4 #define LIBNOTMUCH_MICRO_VERSION 0 From 3b760413c5fb42fb461f2f22711dcde9e92964b2 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Mon, 3 Oct 2016 22:40:22 -0300 Subject: [PATCH 163/179] version: bump to 0.23 --- bindings/python/notmuch/version.py | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/python/notmuch/version.py b/bindings/python/notmuch/version.py index 3e54cc24..b6cd0729 100644 --- a/bindings/python/notmuch/version.py +++ b/bindings/python/notmuch/version.py @@ -1,3 +1,3 @@ # this file should be kept in sync with ../../../version -__VERSION__ = '0.23~rc1' +__VERSION__ = '0.23' SOVERSION = '4' diff --git a/version b/version index 866fd80c..39010d22 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.23~rc1 +0.23 From 22cfd6db9f34dbf48a4ddbf4345b0a149fec7903 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Mon, 3 Oct 2016 22:43:23 -0300 Subject: [PATCH 164/179] NEWS: bump date to today --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 805a0347..1eea5cc5 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -Notmuch 0.23 (UNRELEASED) +Notmuch 0.23 (2016-10-03) ========================= General (Xapian 1.4+) From 6cd6561aabcd24d033b592aa4503aaa3c06d241c Mon Sep 17 00:00:00 2001 From: David Bremner Date: Mon, 3 Oct 2016 22:47:21 -0300 Subject: [PATCH 165/179] debian: changelog stanza for 0.23-1 --- debian/changelog | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/debian/changelog b/debian/changelog index 741bdd36..ed181eca 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +notmuch (0.23-1) experimental; urgency=medium + + * New upstream release + * Bump minor version of libnotmuch4 because of new symbols. + * Several new features, see /usr/share/doc/notmuch/NEWS.gz + + -- David Bremner Mon, 03 Oct 2016 22:46:26 -0300 + notmuch (0.23~rc1-1) experimental; urgency=medium * New upstream release candidate From dbf61427896a17b2f91cf8f79501df420debab8d Mon Sep 17 00:00:00 2001 From: David Bremner Date: Wed, 5 Oct 2016 21:27:17 -0300 Subject: [PATCH 166/179] debian: changelog stanza for 0.23-2 --- debian/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/debian/changelog b/debian/changelog index ed181eca..1c67e864 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +notmuch (0.23-2) unstable; urgency=medium + + * upload to unstable + + -- David Bremner Wed, 05 Oct 2016 21:27:00 -0300 + notmuch (0.23-1) experimental; urgency=medium * New upstream release From af8903df34647d9456e8f3fe26a164959969b451 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 4 Oct 2016 21:35:23 -0300 Subject: [PATCH 167/179] require xapian >= 1.2.6 It seems that no-one tried to compile without Xapian compact support since March of 2015, since that's when I introduced a syntax error in that branch of the ifdef. Given the choice of maintaining this underused branch of code, or bumping the Xapian dependency to a version from 2011, it seems reasonable to do the latter. --- configure | 5 +++-- lib/database.cc | 12 ------------ 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/configure b/configure index cc6746c1..2a0ce9eb 100755 --- a/configure +++ b/configure @@ -394,6 +394,7 @@ EOF printf "Yes.\n" else printf "No.\n" + errors=$((errors + 1)) fi rm -f _compact.o _compact.cc @@ -683,8 +684,8 @@ EOF if [ $have_python -eq 0 ]; then echo " python interpreter" fi - if [ $have_xapian -eq 0 ]; then - echo " Xapian library (including development files such as headers)" + if [ $have_xapian -eq 0 -o $have_xapian_compact -eq 0 ]; then + echo " Xapian library (>= version 1.2.6, including development files such as headers)" echo " https://xapian.org/" fi if [ $have_zlib -eq 0 ]; then diff --git a/lib/database.cc b/lib/database.cc index 4bfae019..f0bfe566 100644 --- a/lib/database.cc +++ b/lib/database.cc @@ -1143,7 +1143,6 @@ notmuch_database_close (notmuch_database_t *notmuch) return status; } -#if HAVE_XAPIAN_COMPACT static int unlink_cb (const char *path, unused (const struct stat *sb), @@ -1327,17 +1326,6 @@ notmuch_database_compact (const char *path, return ret; } -#else -notmuch_status_t -notmuch_database_compact (unused (const char *path), - unused (const char *backup_path), - unused (notmuch_compact_status_cb_t status_cb), - unused (void *closure)) -{ - _notmuch_database_log (notmuch, "notmuch was compiled against a xapian version lacking compaction support.\n"); - return NOTMUCH_STATUS_UNSUPPORTED_OPERATION; -} -#endif notmuch_status_t notmuch_database_destroy (notmuch_database_t *notmuch) From b2d6f07a028a443c2f96e1684be5ec3addb55d96 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 4 Oct 2016 22:12:56 -0300 Subject: [PATCH 168/179] lib: document API added in 0.23 The API was already documented, but for future readers note when the functions were added, --- lib/notmuch.h | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/lib/notmuch.h b/lib/notmuch.h index 251c29b5..18678c0a 100644 --- a/lib/notmuch.h +++ b/lib/notmuch.h @@ -1679,7 +1679,7 @@ notmuch_message_destroy (notmuch_message_t *message); * @returns * - NOTMUCH_STATUS_NULL_POINTER: *value* may not be NULL. * - NOTMUCH_STATUS_SUCCESS: No error occured. - + * @since libnotmuch 4.4 (notmuch 0.23) */ notmuch_status_t notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value); @@ -1691,6 +1691,7 @@ notmuch_message_get_property (notmuch_message_t *message, const char *key, const * - NOTMUCH_STATUS_ILLEGAL_ARGUMENT: *key* may not contain an '=' character. * - NOTMUCH_STATUS_NULL_POINTER: Neither *key* nor *value* may be NULL. * - NOTMUCH_STATUS_SUCCESS: No error occured. + * @since libnotmuch 4.4 (notmuch 0.23) */ notmuch_status_t notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value); @@ -1704,6 +1705,7 @@ notmuch_message_add_property (notmuch_message_t *message, const char *key, const * - NOTMUCH_STATUS_ILLEGAL_ARGUMENT: *key* may not contain an '=' character. * - NOTMUCH_STATUS_NULL_POINTER: Neither *key* nor *value* may be NULL. * - NOTMUCH_STATUS_SUCCESS: No error occured. + * @since libnotmuch 4.4 (notmuch 0.23) */ notmuch_status_t notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value); @@ -1719,6 +1721,7 @@ notmuch_message_remove_property (notmuch_message_t *message, const char *key, co * read-only mode so message cannot be modified. * - NOTMUCH_STATUS_SUCCESS: No error occured. * + * @since libnotmuch 4.4 (notmuch 0.23) */ notmuch_status_t notmuch_message_remove_all_properties (notmuch_message_t *message, const char *key); @@ -1757,6 +1760,8 @@ typedef struct _notmuch_string_map_iterator notmuch_message_properties_t; * notmuch_message_properties_t object. (For consistency, we do * provide a notmuch_message_properities_destroy function, but there's * no good reason to call it if the message is about to be destroyed). + * + * @since libnotmuch 4.4 (notmuch 0.23) */ notmuch_message_properties_t * notmuch_message_get_properties (notmuch_message_t *message, const char *key, notmuch_bool_t exact); @@ -1774,6 +1779,8 @@ notmuch_message_get_properties (notmuch_message_t *message, const char *key, not * See the documentation of notmuch_message_properties_get for example * code showing how to iterate over a notmuch_message_properties_t * object. + * + * @since libnotmuch 4.4 (notmuch 0.23) */ notmuch_bool_t notmuch_message_properties_valid (notmuch_message_properties_t *properties); @@ -1787,6 +1794,8 @@ notmuch_message_properties_valid (notmuch_message_properties_t *properties); * * See the documentation of notmuch_message_get_properties for example * code showing how to iterate over a notmuch_message_properties_t object. + * + * @since libnotmuch 4.4 (notmuch 0.23) */ void notmuch_message_properties_move_to_next (notmuch_message_properties_t *properties); @@ -1795,6 +1804,8 @@ notmuch_message_properties_move_to_next (notmuch_message_properties_t *propertie * Return the key from the current (key,value) pair. * * this could be useful if iterating for a prefix + * + * @since libnotmuch 4.4 (notmuch 0.23) */ const char * notmuch_message_properties_key (notmuch_message_properties_t *properties); @@ -1803,6 +1814,8 @@ notmuch_message_properties_key (notmuch_message_properties_t *properties); * Return the key from the current (key,value) pair. * * This could be useful if iterating for a prefix. + * + * @since libnotmuch 4.4 (notmuch 0.23) */ const char * notmuch_message_properties_value (notmuch_message_properties_t *properties); @@ -1814,6 +1827,8 @@ notmuch_message_properties_value (notmuch_message_properties_t *properties); * It's not strictly necessary to call this function. All memory from * the notmuch_message_properties_t object will be reclaimed when the * containing message object is destroyed. + * + * @since libnotmuch 4.4 (notmuch 0.23) */ void notmuch_message_properties_destroy (notmuch_message_properties_t *properties); @@ -2012,6 +2027,7 @@ notmuch_filenames_destroy (notmuch_filenames_t *filenames); /** * set config 'key' to 'value' * + * @since libnotmuch 4.4 (notmuch 0.23) */ notmuch_status_t notmuch_database_set_config (notmuch_database_t *db, const char *key, const char *value); @@ -2024,18 +2040,24 @@ notmuch_database_set_config (notmuch_database_t *db, const char *key, const char * * return value is allocated by malloc and should be freed by the * caller. + * + * @since libnotmuch 4.4 (notmuch 0.23) */ notmuch_status_t notmuch_database_get_config (notmuch_database_t *db, const char *key, char **value); /** * Create an iterator for all config items with keys matching a given prefix + * + * @since libnotmuch 4.4 (notmuch 0.23) */ notmuch_status_t notmuch_database_get_config_list (notmuch_database_t *db, const char *prefix, notmuch_config_list_t **out); /** * Is 'config_list' iterator valid (i.e. _key, _value, _move_to_next can be called). + * + * @since libnotmuch 4.4 (notmuch 0.23) */ notmuch_bool_t notmuch_config_list_valid (notmuch_config_list_t *config_list); @@ -2045,6 +2067,8 @@ notmuch_config_list_valid (notmuch_config_list_t *config_list); * * return value is owned by the iterator, and will be destroyed by the * next call to notmuch_config_list_key or notmuch_config_list_destroy. + * + * @since libnotmuch 4.4 (notmuch 0.23) */ const char * notmuch_config_list_key (notmuch_config_list_t *config_list); @@ -2054,6 +2078,8 @@ notmuch_config_list_key (notmuch_config_list_t *config_list); * * return value is owned by the iterator, and will be destroyed by the * next call to notmuch_config_list_value or notmuch config_list_destroy + * + * @since libnotmuch 4.4 (notmuch 0.23) */ const char * notmuch_config_list_value (notmuch_config_list_t *config_list); @@ -2061,18 +2087,24 @@ notmuch_config_list_value (notmuch_config_list_t *config_list); /** * move 'config_list' iterator to the next pair + * + * @since libnotmuch 4.4 (notmuch 0.23) */ void notmuch_config_list_move_to_next (notmuch_config_list_t *config_list); /** * free any resources held by 'config_list' + * + * @since libnotmuch 4.4 (notmuch 0.23) */ void notmuch_config_list_destroy (notmuch_config_list_t *config_list); /** * interrogate the library for compile time features + * + * @since libnotmuch 4.4 (notmuch 0.23) */ notmuch_bool_t notmuch_built_with (const char *name); From f575a346df09c82691bb9e7c462836d982fe31f7 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 9 Oct 2016 19:30:44 -0300 Subject: [PATCH 169/179] emacs/show: force notmuch-show-buttonise-links to act on lines This seems to fix a problem with emacs 25 creating partial buttons by calling n-s-b-l with a region that does not include the whole button. I'm not 100% sure it's legit to act outside the region passed by jit-lock, but goto-address-fontify-region (where I borrowed the code from) already does this, so this patch to not make things worse. --- emacs/notmuch-show.el | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index 641398dc..e7d16f81 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -1174,13 +1174,15 @@ This also turns id:\"\"-parts and mid: links into buttons for a corresponding notmuch search." (goto-address-fontify-region start end) (save-excursion - (let (links) - (goto-char start) - (while (re-search-forward notmuch-id-regexp end t) + (let (links + (beg-line (progn (goto-char start) (line-beginning-position))) + (end-line (progn (goto-char end) (line-end-position)))) + (goto-char beg-line) + (while (re-search-forward notmuch-id-regexp end-line t) (push (list (match-beginning 0) (match-end 0) (match-string-no-properties 0)) links)) - (goto-char start) - (while (re-search-forward notmuch-mid-regexp end t) + (goto-char beg-line) + (while (re-search-forward notmuch-mid-regexp end-line t) (let* ((mid-cid (match-string-no-properties 1)) (mid (save-match-data (string-match "^[^/]*" mid-cid) From a4331bf13859a91ee7b95013954be5c68a70815c Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Sat, 15 Oct 2016 10:40:48 +0100 Subject: [PATCH 170/179] emacs: fix notmuch-search-line-faces defcustom In commit 2a7b11b064233afc4feead876fa396e3c18a6b91 the default value for notmuch-search-line-faces was changed so that it didn't match the specification in the corresponding defcustom. This meant that it was difficult for the user to customize this variable as they got a type mismatch error. Note anyone who had already customised this variable would not see this bug as their customisation would match the defcustom. --- emacs/notmuch.el | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/emacs/notmuch.el b/emacs/notmuch.el index 8e146924..a7e0f2c1 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -669,9 +669,16 @@ of the result." (goto-char (point-min)) (forward-line (1- notmuch-search-target-line))))))))) +(define-widget 'notmuch--custom-face-edit 'lazy + "Custom face edit with a tag Edit Face" + ;; I could not persuage custom-face-edit to respect the :tag + ;; property so create a widget specially + :tag "Manually specify face" + :type 'custom-face-edit) + (defcustom notmuch-search-line-faces - '(("unread" 'notmuch-search-unread-face) - ("flagged" 'notmuch-search-flagged-face)) + '(("unread" . notmuch-search-unread-face) + ("flagged" . notmuch-search-flagged-face)) "Alist of tags to faces for line highlighting in notmuch-search. Each element looks like (TAG . FACE). A thread with TAG will have FACE applied. @@ -689,7 +696,9 @@ matching tags are merged, with earlier attributes overriding later. A message having both \"deleted\" and \"unread\" tags with the above settings would have a green foreground and blue background." - :type '(alist :key-type (string) :value-type (custom-face-edit)) + :type '(alist :key-type (string) + :value-type (radio (face :tag "Face name") + (notmuch--custom-face-edit))) :group 'notmuch-search :group 'notmuch-faces) From 4c822aecc75bd6f7cdffc80b704b27dde8e27ea6 Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Wed, 12 Oct 2016 08:39:21 +0100 Subject: [PATCH 171/179] emacs: search face bugfix In commit 2a7b11b064233afc4feead876fa396e3c18a6b91 the default faces for unread and flagged were accidentally swapped. This swaps them back. --- emacs/notmuch.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/emacs/notmuch.el b/emacs/notmuch.el index a7e0f2c1..9c7f2020 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -313,7 +313,7 @@ there will be called at other points of notmuch execution." (defface notmuch-search-flagged-face '((t - (:weight bold))) + (:foreground "blue"))) "Face used in search mode face for flagged threads. This face is the default value for the \"flagged\" tag in @@ -323,7 +323,7 @@ This face is the default value for the \"flagged\" tag in (defface notmuch-search-unread-face '((t - (:foreground "blue"))) + (:weight bold))) "Face used in search mode for unread threads. This face is the default value for the \"unread\" tag in From e2e0b451fe92c0419b47478e9d0345916ca239c2 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Tue, 11 Oct 2016 23:01:56 -0300 Subject: [PATCH 172/179] NEWS for 0.23.1 --- NEWS | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/NEWS b/NEWS index 1eea5cc5..ac3ceb10 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,22 @@ +Notmuch 0.23.1 (UNRELEASED) +=========================== + +General +------- + +Require Xapian >= 1.2.6 + + The ifdef branch for older Xapian (pre-compact API) had bit-rotted. + +Emacs +----- + +Fix test failure with Emacs 25.1 + + A previously undiscovered jit-lock related bug was exposed by Emacs + 25, causing a notmuch-show mode test to fail. This release fixes the + bug, and hence the test. + Notmuch 0.23 (2016-10-03) ========================= From a3ee732c522947a17329704aef2a099946420f52 Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Thu, 20 Oct 2016 11:19:44 +0300 Subject: [PATCH 173/179] test-lib.sh: rename $DTACH_TERM to $SMART_TERM --- test/test-lib.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test-lib.sh b/test/test-lib.sh index bda8a80a..77879c25 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -57,7 +57,7 @@ ORIGINAL_TERM=$TERM # dtach(1) provides more capable terminal environment to anything # that requires more than dumb terminal... -[ x"${TERM:-dumb}" = xdumb ] && DTACH_TERM=vt100 || DTACH_TERM=$TERM +[ x"${TERM:-dumb}" = xdumb ] && SMART_TERM=vt100 || SMART_TERM=$TERM # For repeatability, reset the environment to known value. LANG=C @@ -1171,7 +1171,7 @@ test_emacs () { # user's TERM (or 'vt100' in case user's TERM is unset, empty # or 'dumb') is given to dtach which assumes a minimally # VT100-compatible terminal -- and emacs inherits that - TERM=$DTACH_TERM dtach -n "$TEST_TMPDIR/emacs-dtach-socket.$$" \ + TERM=$SMART_TERM dtach -n "$TEST_TMPDIR/emacs-dtach-socket.$$" \ sh -c "stty rows 24 cols 80; exec '$TMP_DIRECTORY/run_emacs' \ --no-window-system \ $load_emacs_tests \ From c008dd13523bb1356653fa8678cfe5d26a6feb82 Mon Sep 17 00:00:00 2001 From: Tomi Ollila Date: Thu, 20 Oct 2016 11:19:45 +0300 Subject: [PATCH 174/179] test: use vt100 as "smart" terminal for known dumb/unknown terminals Otherwise use whatever user environment has set for TERM so that there is more chance to test on users' actual environments. --- test/test-lib.sh | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/test/test-lib.sh b/test/test-lib.sh index 77879c25..e7b83392 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -55,9 +55,15 @@ export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' # Keep the original TERM for say_color and test_emacs ORIGINAL_TERM=$TERM -# dtach(1) provides more capable terminal environment to anything -# that requires more than dumb terminal... -[ x"${TERM:-dumb}" = xdumb ] && SMART_TERM=vt100 || SMART_TERM=$TERM +# Set SMART_TERM to vt100 for known dumb/unknown terminal. +# Otherwise use whatever TERM is currently used so that +# users' actual TERM environments are being used in tests. +case ${TERM-} in + '' | dumb | unknown ) + SMART_TERM=vt100 ;; + *) + SMART_TERM=$TERM ;; +esac # For repeatability, reset the environment to known value. LANG=C @@ -1168,8 +1174,8 @@ test_emacs () { fi server_name="notmuch-test-suite-$$" # start a detached session with an emacs server - # user's TERM (or 'vt100' in case user's TERM is unset, empty - # or 'dumb') is given to dtach which assumes a minimally + # user's TERM (or 'vt100' in case user's TERM is known dumb + # or unknown) is given to dtach which assumes a minimally # VT100-compatible terminal -- and emacs inherits that TERM=$SMART_TERM dtach -n "$TEST_TMPDIR/emacs-dtach-socket.$$" \ sh -c "stty rows 24 cols 80; exec '$TMP_DIRECTORY/run_emacs' \ From 4bf3bb31dd866245005c0b2079e0ef1cf9c994ac Mon Sep 17 00:00:00 2001 From: Keith Amidon Date: Wed, 19 Oct 2016 03:05:19 -0700 Subject: [PATCH 175/179] Expand docstrings about fcc using notmuch-insert This commit expands docstrings for notmuch-fcc-dirs and notmuch-maildir-fcc-with-notmuch-insert to describe how quoted strings are processed and make the ability to configure sent folders containing whitespace more discoverable. --- emacs/notmuch-maildir-fcc.el | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/emacs/notmuch-maildir-fcc.el b/emacs/notmuch-maildir-fcc.el index 95e56503..ea75bb9e 100644 --- a/emacs/notmuch-maildir-fcc.el +++ b/emacs/notmuch-maildir-fcc.el @@ -54,7 +54,10 @@ If `notmuch-maildir-use-notmuch-insert' is set (the default) then the header should be of the form \"folder +tag1 -tag2\" where folder is the folder (relative to the notmuch mailstore) to store the message in, and tag1 and tag2 are tag changes to apply to the -stored message. +stored message. This string is split using `split-string-and-unquote', +so a folder name containing spaces can be specified by +quoting each space with an immediately preceding backslash +or surrounding the entire folder name in double quotes. If `notmuch-maildir-use-notmuch-insert' is nil then the Fcc header should be the directory where the message should be @@ -230,8 +233,12 @@ should be a list of tag changes to apply to the inserted message." The fcc-header should be of the form \"folder +tag1 -tag2\" where folder is the folder (relative to the notmuch mailstore) to store the message in, and tag1 and tag2 are tag changes to apply to the -stored message. If CREATE is non-nil then create the folder if -necessary." +stored message. This string is split using `split-string-and-unquote', +so a folder name containing spaces can be specified by +quoting each space with an immediately preceding backslash +or surrounding the entire folder name in double quotes. + +If CREATE is non-nil then create the folder if necessary." (let* ((args (split-string-and-unquote fcc-header)) (folder (car args)) (tags (cdr args))) From 9b1538f3b21cd27208c5dd08a20c84659d231669 Mon Sep 17 00:00:00 2001 From: Mark Walters Date: Sat, 22 Oct 2016 07:45:17 +0100 Subject: [PATCH 176/179] NEWS for two bugfixes This adds news items for the two bugs emacs: search face bugfix and emacs: fix notmuch-search-line-faces defcustom --- NEWS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/NEWS b/NEWS index ac3ceb10..547b961c 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,15 @@ Require Xapian >= 1.2.6 Emacs ----- +Fix default colours for unread and flagged messages + + In 0.23 the default colours for unread and flagged messages in + search view were accidentally swapped. This release returns them to + the original colours. + + A related change in 0.23 broke the customize widget for + notmuch-search-line-faces. This is now fixed. + Fix test failure with Emacs 25.1 A previously undiscovered jit-lock related bug was exposed by Emacs From f053bb8bd9c7f4b8263a6c099d76839ff38701d5 Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sat, 22 Oct 2016 12:35:25 -0300 Subject: [PATCH 177/179] version: bump to 0.23.1 --- bindings/python/notmuch/version.py | 2 +- version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/python/notmuch/version.py b/bindings/python/notmuch/version.py index b6cd0729..4bbe0cdc 100644 --- a/bindings/python/notmuch/version.py +++ b/bindings/python/notmuch/version.py @@ -1,3 +1,3 @@ # this file should be kept in sync with ../../../version -__VERSION__ = '0.23' +__VERSION__ = '0.23.1' SOVERSION = '4' diff --git a/version b/version index 39010d22..610e2872 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.23 +0.23.1 From 4f032e788af2227d3e857af24f072b5bac69717c Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sat, 22 Oct 2016 12:39:11 -0300 Subject: [PATCH 178/179] debian: start changelog stanza for 0.23.1 --- debian/changelog | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/debian/changelog b/debian/changelog index 1c67e864..361955c9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,13 @@ +notmuch (0.23.1-1) UNRELEASED; urgency=medium + + * New upstream bugfix release + * Fix test suite for Emacs 25.1 + * Fix some Emacs customization regressions introduced in 0.23 + * Bug fix: "testsuite fails with TERM=unknown", thanks to Gianfranco + Costamagna (Closes: #841319). + + -- David Bremner Sat, 22 Oct 2016 12:37:49 -0300 + notmuch (0.23-2) unstable; urgency=medium * upload to unstable From ad517e9195a29b26955999c6e11fc37c73dbc01e Mon Sep 17 00:00:00 2001 From: David Bremner Date: Sun, 23 Oct 2016 22:06:39 -0300 Subject: [PATCH 179/179] finalize release 0.23.1 --- NEWS | 2 +- debian/changelog | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 547b961c..846572be 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -Notmuch 0.23.1 (UNRELEASED) +Notmuch 0.23.1 (2016-10-23) =========================== General diff --git a/debian/changelog b/debian/changelog index 361955c9..2c7cd234 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -notmuch (0.23.1-1) UNRELEASED; urgency=medium +notmuch (0.23.1-1) unstable; urgency=medium * New upstream bugfix release * Fix test suite for Emacs 25.1 @@ -6,7 +6,7 @@ notmuch (0.23.1-1) UNRELEASED; urgency=medium * Bug fix: "testsuite fails with TERM=unknown", thanks to Gianfranco Costamagna (Closes: #841319). - -- David Bremner Sat, 22 Oct 2016 12:37:49 -0300 + -- David Bremner Sun, 23 Oct 2016 22:06:12 -0300 notmuch (0.23-2) unstable; urgency=medium