]> git.notmuchmail.org Git - notmuch/commitdiff
Merge branch 'release'
authorDavid Bremner <david@tethera.net>
Wed, 6 Mar 2019 12:53:26 +0000 (08:53 -0400)
committerDavid Bremner <david@tethera.net>
Wed, 6 Mar 2019 12:53:26 +0000 (08:53 -0400)
Changes from 0.28.3

31 files changed:
NEWS
bindings/python/notmuch/message.py
configure
debugger.c
doc/Makefile.local
doc/man1/notmuch-address.rst
doc/man1/notmuch-count.rst
doc/man1/notmuch-search.rst
doc/man1/notmuch-show.rst
doc/notmuch-emacs.rst
emacs/.gitignore
emacs/Makefile.local
emacs/notmuch-lib.el
emacs/notmuch-mua.el
emacs/notmuch-show.el
emacs/notmuch-wash.el
emacs/notmuch.el
emacs/rstdoc.el [new file with mode: 0644]
emacs/rstdoc.rsti [new file with mode: 0644]
gmime-filter-reply.c
lib/index.cc
lib/notmuch.h
notmuch-reply.c
notmuch-show.c
test/.gitignore
test/T190-multipart.sh
test/T220-reply.sh
test/T230-reply-to-sender.sh
test/T310-emacs.sh
test/T350-crypto.sh
test/T720-lib-lifetime.sh [new file with mode: 0755]

diff --git a/NEWS b/NEWS
index 549b1840a8cf0306b0a02f0f00571bfca521dd5f..9fcb57edc3d3c830cdc9176e6148b7a77d40f12e 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,12 @@
+Notmuch 0.29 (UNRELEASED)
+=========================
+
+Command Line Interface
+----------------------
+
+`notmuch show` now supports --body=false and --include-html with
+--format=text
+
 Notmuch 0.28.3 (2019-03-05)
 ===========================
 
index de0fb4151e52e50c3f3eb684932b65820d74cf82..6e32b5f7ada2d825d4cd6c23403f6b6b1271691f 100644 (file)
@@ -482,7 +482,9 @@ class Message(Python3StringMixIn):
         if status != 0:
             raise NotmuchError(status)
 
-        return value.value.decode('utf-8') if value is not None else None
+        if value is None or value.value is None:
+            return None
+        return value.value.decode('utf-8')
 
     def get_properties(self, prop="", exact=False):
         """ Get the properties of the message, returning a generator of
index ea22587b24ff257bfe8d2b2dff3e27e7d9d2f2f2..a6f5dfa2ca2b894da29ff4bd93ef96a3266587ab 100755 (executable)
--- a/configure
+++ b/configure
@@ -623,8 +623,8 @@ if [ -z "${EMACSETCDIR-}" ]; then
     EMACSETCDIR="\$(prefix)/share/emacs/site-lisp"
 fi
 
-printf "Checking if emacs is available... "
-if emacs --quick --batch > /dev/null 2>&1; then
+printf "Checking if emacs (>= 24) is available... "
+if emacs --quick --batch --eval '(if (< emacs-major-version 24) (kill-emacs 1))' > /dev/null 2>&1; then
     printf "Yes.\n"
     have_emacs=1
 else
index 5cb38ac444e35d774152b64973790c4fd5ec7f4d..0febf170d729167b6697f18fffaae467d8c84259 100644 (file)
@@ -32,13 +32,14 @@ bool
 debugger_is_active (void)
 {
     char buf[1024];
+    char buf2[1024];
 
     if (RUNNING_ON_VALGRIND)
        return true;
 
     sprintf (buf, "/proc/%d/exe", getppid ());
-    if (readlink (buf, buf, sizeof (buf)) != -1 &&
-       strncmp (basename (buf), "gdb", 3) == 0)
+    if (readlink (buf, buf2, sizeof (buf2)) != -1 &&
+       strncmp (basename (buf2), "gdb", 3) == 0)
     {
        return true;
     }
index cb0f1f6488f6d9cbe0e3e8d345c5d90053c2de9c..bab3d0d286ce75efe68433b3a8c82ab87eac7dd6 100644 (file)
@@ -37,6 +37,10 @@ INFO_INFO_FILES := $(INFO_TEXI_FILES:.texi=.info)
 %.gz: %
        rm -f $@ && gzip --stdout $^ > $@
 
+ifeq ($(WITH_EMACS),1)
+$(DOCBUILDDIR)/.roff.stamp sphinx-html sphinx-texinfo: docstring.stamp
+endif
+
 # Sequentialize the calls to sphinx-build to avoid races with
 # reading/writing cached state. This uses GNU make specific
 # "order-only" prerequisites.
index 12d86e8952c5e70f5959097930b5a3607094597b..2a7df6f0d80f21133fee3f58902f907e83d7a34b 100644 (file)
@@ -92,7 +92,7 @@ Supported options for **address** include
 
 ``--exclude=(true|false)``
     A message is called "excluded" if it matches at least one tag in
-    search.tag\_exclude that does not appear explicitly in the search
+    search.exclude\_tags that does not appear explicitly in the search
     terms. This option specifies whether to omit excluded messages in
     the search process.
 
index 9ca20dab274733ed0a049f83f55e5cd83b25951b..0eac5dbef4ba09242795cf14abc3de2affe96444 100644 (file)
@@ -36,7 +36,7 @@ Supported options for **count** include
         same message-id).
 
 ``--exclude=(true|false)``
-    Specify whether to omit messages matching search.tag\_exclude from
+    Specify whether to omit messages matching search.exclude\_tags from
     the count (the default) or not.
 
 ``--batch``
index 654c5f2cfbcab5cdc6f798cd873110ec1813a8c2..ed9ff4e5b96522751b056192bb7eabc5b0da75dd 100644 (file)
@@ -100,7 +100,7 @@ Supported options for **search** include
 
 ``--exclude=(true|false|all|flag)``
     A message is called "excluded" if it matches at least one tag in
-    search.tag\_exclude that does not appear explicitly in the search
+    search.exclude\_tags that does not appear explicitly in the search
     terms. This option specifies whether to omit excluded messages in
     the search process.
 
index 8bfa87c664f98124bbbe5a763cfb45e0c6603589..becd3e79929023b089a9e5542c15380961aae58f 100644 (file)
@@ -161,7 +161,7 @@ Supported options for **show** include
     Default: ``auto``
 
 ``--exclude=(true|false)``
-    Specify whether to omit threads only matching search.tag\_exclude
+    Specify whether to omit threads only matching search.exclude\_tags
     from the search results (the default) or not. In either case the
     excluded message will be marked with the exclude flag (except when
     output=mbox when there is nowhere to put the flag).
@@ -176,18 +176,19 @@ Supported options for **show** include
 ``--body=(true|false)``
     If true (the default) **notmuch show** includes the bodies of the
     messages in the output; if false, bodies are omitted.
-    ``--body=false`` is only implemented for the json and sexp formats
-    and it is incompatible with ``--part > 0.``
+    ``--body=false`` is only implemented for the text, json and sexp
+    formats and it is incompatible with ``--part > 0.``
 
     This is useful if the caller only needs the headers as body-less
     output is much faster and substantially smaller.
 
 ``--include-html``
-    Include "text/html" parts as part of the output (currently only
-    supported with ``--format=json`` and ``--format=sexp``). By default,
-    unless ``--part=N`` is used to select a specific part or
-    ``--include-html`` is used to include all "text/html" parts, no
-    part with content type "text/html" is included in the output.
+    Include "text/html" parts as part of the output (currently
+    only supported with ``--format=text``, ``--format=json`` and
+    ``--format=sexp``). By default, unless ``--part=N`` is used to
+    select a specific part or ``--include-html`` is used to include all
+    "text/html" parts, no part with content type "text/html" is included
+    in the output.
 
 A common use of **notmuch show** is to display a single thread of email
 messages. For this, use a search term of "thread:<thread-id>" as can be
index ce2e358e2134e2dfb4c86a2fe89f2d00a7eb4f45..0df46df9e700fd1bd5bc046697b1247d8bfa9646 100644 (file)
@@ -62,6 +62,7 @@ notmuch-hello key bindings
 ``<return>``
     Activate the current widget.
 
+``g``
 ``=``
     Refresh the buffer; mainly update the counts of messages for various
     saved searches.
@@ -159,6 +160,10 @@ menu of results that the user can explore further by pressing
 ``<return>``
     Open thread on current line in :ref:`notmuch-show` mode
 
+``g``
+``=``
+    Refresh the buffer
+
 ``?``
     Display full set of key bindings
 
@@ -190,6 +195,9 @@ pressing RET after positioning the cursor on a hidden part.
     advance to the next message, or advance to the next thread (if
     already on the last message of a thread).
 
+``c``
+    :ref:`show-copy`
+
 ``N``
     Move to next message
 
@@ -208,6 +216,63 @@ pressing RET after positioning the cursor on a hidden part.
 ``?``
     Display full set of key bindings
 
+Display of messages can be controlled by the following variables
+
+:index:`notmuch-message-headers`
+       |docstring::notmuch-message-headers|
+
+:index:`notmuch-message-headers-visible`
+       |docstring::notmuch-message-headers-visible|
+
+.. _show-copy:
+
+Copy to kill-ring
+-----------------
+
+You can use the usually Emacs ways of copying text to the kill-ring,
+but notmuch also provides some shortcuts. These keys are available in
+:ref:`notmuch-show`, and :ref:`notmuch-tree`. A subset are available
+in :ref:`notmuch-search`.
+
+``c F``        ``notmuch-show-stash-filename``
+   |docstring::notmuch-show-stash-filename|
+
+``c G`` ``notmuch-show-stash-git-send-email``
+   |docstring::notmuch-show-stash-git-send-email|
+
+``c I`` ``notmuch-show-stash-message-id-stripped``
+   |docstring::notmuch-show-stash-message-id-stripped|
+
+``c L`` ``notmuch-show-stash-mlarchive-link-and-go``
+   |docstring::notmuch-show-stash-mlarchive-link-and-go|
+
+``c T`` ``notmuch-show-stash-tags``
+   |docstring::notmuch-show-stash-tags|
+
+``c c`` ``notmuch-show-stash-cc``
+   |docstring::notmuch-show-stash-cc|
+
+``c d`` ``notmuch-show-stash-date``
+   |docstring::notmuch-show-stash-date|
+
+``c f`` ``notmuch-show-stash-from``
+   |docstring::notmuch-show-stash-from|
+
+``c i`` ``notmuch-show-stash-message-id``
+   |docstring::notmuch-show-stash-message-id|
+
+``c l`` ``notmuch-show-stash-mlarchive-link``
+   |docstring::notmuch-show-stash-mlarchive-link|
+
+``c s`` ``notmuch-show-stash-subject``
+   |docstring::notmuch-show-stash-subject|
+
+``c t`` ``notmuch-show-stash-to``
+   |docstring::notmuch-show-stash-to|
+
+``c ?``
+    Show all available copying commands
+
 .. _notmuch-tree:
 
 notmuch-tree
@@ -218,6 +283,9 @@ email archives. Each line in the buffer represents a single
 message giving the relative date, the author, subject, and any
 tags.
 
+``c``
+    :ref:`show-copy`
+
 ``<return>``
    Displays that message.
 
@@ -233,6 +301,10 @@ tags.
 ``p``
     Move to previous matching message
 
+``g``
+``=``
+    Refresh the buffer
+
 ``?``
     Display full set of key bindings
 
@@ -275,7 +347,13 @@ operations specified in ``notmuch-tagging-keys``; i.e. each
 
 :index:`notmuch-tagging-keys`
 
-   A list of keys and corresponding tagging operations.
+  |docstring::notmuch-tagging-keys|
+
+Buffer navigation
+=================
+
+:index:`notmuch-cycle-notmuch-buffers`
+   |docstring::notmuch-cycle-notmuch-buffers|
 
 Configuration
 =============
@@ -286,8 +364,10 @@ Importing Mail
 --------------
 
 :index:`notmuch-poll`
+   |docstring::notmuch-poll|
 
 :index:`notmuch-poll-script`
+   |docstring::notmuch-poll-script|
 
 Init File
 ---------
@@ -300,3 +380,13 @@ suffix exist it will be read instead (just one of these, chosen in this
 order). Most often users create ``~/.emacs.d/notmuch-config.el`` and just
 work with it. If Emacs was invoked with the ``-q`` or ``--no-init-file``
 options, ``notmuch-init-file`` is not read.
+
+.. include:: ../emacs/rstdoc.rsti
+
+.. include:: ../emacs/notmuch.rsti
+
+.. include:: ../emacs/notmuch-lib.rsti
+
+.. include:: ../emacs/notmuch-show.rsti
+
+.. include:: ../emacs/notmuch-tag.rsti
index fbf8dde6f1760c409036d6b9b473a59d3c943ae6..b9873b0ab6667eedf018150217f8ba2d9f1eaa83 100644 (file)
@@ -1,4 +1,5 @@
 /.eldeps*
 /*.elc
+/*.rsti
 /notmuch-version.el
 /notmuch-pkg.el
index 5e4ae1bda257f61cac0f296ee5a9ff2bec80f4f9..04913a06bf61387204d8bbd00feb88ddd25fa072 100644 (file)
@@ -45,6 +45,10 @@ emacs_images := \
        $(srcdir)/$(dir)/notmuch-logo.png
 
 emacs_bytecode = $(emacs_sources:.el=.elc)
+emacs_docstrings = $(emacs_sources:.el=.rsti)
+
+docstring.stamp: ${emacs_docstrings}
+       touch $@
 
 # Because of defmacro's and defsubst's, we have to account for load
 # dependencies between Elisp files when byte compiling.  Otherwise,
@@ -76,6 +80,8 @@ CLEAN+=$(dir)/.eldeps $(dir)/.eldeps.tmp $(dir)/.eldeps.x
 ifeq ($(HAVE_EMACS),1)
 %.elc: %.el $(global_deps)
        $(call quiet,EMACS) --directory emacs -batch -f batch-byte-compile $<
+%.rsti: %.el
+       $(call quiet,EMACS) -batch -L emacs -l rstdoc -f rstdoc-batch-extract $< $@
 endif
 
 elpa: $(ELPA_FILE)
@@ -93,7 +99,7 @@ endif
 
 ifeq ($(WITH_EMACS),1)
 ifeq ($(HAVE_EMACS),1)
-all: $(emacs_bytecode)
+all: $(emacs_bytecode) $(emacs_docstrings)
 install-emacs: $(emacs_bytecode)
 endif
 
@@ -120,4 +126,5 @@ ifeq ($(WITH_DESKTOP),1)
        -update-desktop-database "$(DESTDIR)$(desktop_dir)"
 endif
 
-CLEAN := $(CLEAN) $(emacs_bytecode) $(dir)/notmuch-version.el $(dir)/notmuch-pkg.el
+CLEAN := $(CLEAN) $(emacs_bytecode) $(dir)/notmuch-version.el $(dir)/notmuch-pkg.el \
+       $(emacs_docstrings) docstring.stamp
index 25d83fd61b49ca01aaa129de9f3ead93bec30ae6..8cf7261e6f4fac2d16a9c7c2aafc96866b29d90c 100644 (file)
@@ -155,6 +155,7 @@ For example, if you wanted to remove an \"inbox\" tag and add an
     (define-key map "s" 'notmuch-search)
     (define-key map "z" 'notmuch-tree)
     (define-key map "m" 'notmuch-mua-new-mail)
+    (define-key map "g" 'notmuch-refresh-this-buffer)
     (define-key map "=" 'notmuch-refresh-this-buffer)
     (define-key map (kbd "M-=") 'notmuch-refresh-all-buffers)
     (define-key map "G" 'notmuch-poll-and-refresh-this-buffer)
index df2ac4470f5200fcfb183f5c1cf01c5c84a15c24..e205fa4c92ff9ce7451e1e9c908acac101a978aa 100644 (file)
@@ -306,7 +306,7 @@ modified. This function is notmuch addaptation of
          (if window
              ;; Raise the frame already displaying the message buffer.
              (progn
-               (gnus-select-frame-set-input-focus (window-frame window))
+               (select-frame-set-input-focus (window-frame window))
                (select-window window))
            (funcall switch-function buffer)
            (set-buffer buffer))
index a0a58373aa675a330dc57c04f94bcde622a0f53e..78f1af4710a70dcc4deaa913ddd81b2bc2807781 100644 (file)
@@ -604,7 +604,7 @@ will return nil if the CID is unknown or cannot be retrieved."
     (unless (assq 'notmuch-show-mode w3m-cid-retrieve-function-alist)
       (push (cons 'notmuch-show-mode #'notmuch-show--cid-w3m-retrieve)
            w3m-cid-retrieve-function-alist)))
-  (setq mm-inline-text-html-with-images t))
+  (setq mm-html-inhibit-images nil))
 
 (defvar w3m-current-buffer) ;; From `w3m.el'.
 (defun notmuch-show--cid-w3m-retrieve (url &rest args)
index 5f8b92671b39ac2419ef4808e27296d062bc885a..54108d93607bf83b228f5f2b17ada5f215e62192 100644 (file)
@@ -24,7 +24,7 @@
 ;;; Code:
 
 (require 'coolj)
-
+(require 'notmuch-lib)
 (declare-function notmuch-show-insert-bodypart "notmuch-show" (msg part depth &optional hide))
 (defvar notmuch-show-indent-messages-width)
 
index 44402f8aa8256b55816b9a256b6b7f7468e54e9d..804e78ab8c53cf855cd9a5ea3713f19b40da4886 100644 (file)
@@ -711,7 +711,7 @@ 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):
 
- (setq notmuch-search-line-faces '((\"unread\" . (:foreground \"green\"))
+ (setq notmuch-search-line-faces \\='((\"unread\" . (:foreground \"green\"))
                                    (\"deleted\" . (:foreground \"red\"
                                                  :background \"blue\"))))
 
@@ -1076,7 +1076,7 @@ current search results AND the additional query string provided."
 Runs a new search matching only messages that match both the
 current search results AND that are tagged with the given tag."
   (interactive
-   (list (notmuch-select-tag-with-completion "Filter by tag: ")))
+   (list (notmuch-select-tag-with-completion "Filter by tag: " notmuch-search-query-string)))
   (notmuch-search (concat notmuch-search-query-string " and tag:" tag) notmuch-search-oldest-first))
 
 ;;;###autoload
diff --git a/emacs/rstdoc.el b/emacs/rstdoc.el
new file mode 100644 (file)
index 0000000..2225aef
--- /dev/null
@@ -0,0 +1,85 @@
+;;; rstdoc.el --- help generate documentation from docstrings -*-lexical-binding: t-*-
+
+;; Copyright (C) 2018 David Bremner
+
+;; Author: David Bremner <david@tethera.net>
+;; Created: 26 May 2018
+;; Keywords: emacs lisp, documentation
+;; Homepage: https://notmuchmail.org
+
+;; This file is not part of GNU Emacs.
+
+;; rstdoc.el 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.
+;;
+;; rstdoc.el 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 rstdoc.el.  If not, see <https://www.gnu.org/licenses/>.
+;;
+
+;;; Commentary:
+;;
+
+;; Rstdoc provides a facility to extract all of the docstrings defined in
+;; an elisp source file. Usage:
+;;
+;; emacs -Q --batch -L . -l rstdoc -f rstdoc-batch-extract foo.el foo.rsti
+
+;;; Code:
+
+(provide 'rstdoc)
+
+(defun rstdoc-batch-extract ()
+  "Extract docstrings to and from the files on the command line"
+  (apply #'rstdoc-extract command-line-args-left))
+
+(defun rstdoc-extract (in-file out-file)
+  "Write docstrings from IN-FILE to OUT-FILE"
+  (load-file in-file)
+  (let* ((definitions (cdr (assoc (expand-file-name in-file) load-history)))
+        (doc-hash (make-hash-table :test 'eq)))
+    (mapc
+     (lambda (elt)
+       (let ((pair
+             (pcase elt
+               (`(defun . ,name) (cons name (documentation name)))
+               (`(,_ . ,_)  nil)
+               (sym (cons sym (get sym 'variable-documentation))))))
+        (when (and pair (cdr pair))
+          (puthash (car pair) (cdr pair) doc-hash))))
+     definitions)
+    (with-temp-buffer
+      (maphash
+       (lambda (key val)
+        (rstdoc--insert-docstring key val))
+       doc-hash)
+      (write-region (point-min) (point-max) out-file))))
+
+(defun rstdoc--insert-docstring (symbol docstring)
+  (insert (format "\n.. |docstring::%s| replace::\n" symbol))
+  (insert (replace-regexp-in-string "^" "    " (rstdoc--rst-quote-string docstring)))
+  (insert "\n"))
+
+(defvar rst--escape-alist
+  '( ("\\\\='" . "\\\\'")
+     ("\\([^\\]\\)'" . "\\1`")
+     ("^[[:space:]\t]*$" . "|br|")
+     ("^[[:space:]\t]" . "|indent| "))
+    "list of (regex . replacement) pairs")
+
+(defun rstdoc--rst-quote-string (str)
+  (with-temp-buffer
+    (insert str)
+    (dolist (pair rst--escape-alist)
+      (goto-char (point-min))
+      (while (re-search-forward (car pair) nil t)
+       (replace-match (cdr pair))))
+    (buffer-substring (point-min) (point-max))))
+
+;;; rstdoc.el ends here
diff --git a/emacs/rstdoc.rsti b/emacs/rstdoc.rsti
new file mode 100644 (file)
index 0000000..a449b58
--- /dev/null
@@ -0,0 +1,21 @@
+.. -*- rst -*-
+
+.. |br| replace:: |br-texinfo| |br-html|
+       
+.. |br-texinfo| raw:: texinfo
+
+   @* @*
+
+.. |br-html| raw:: html
+
+   <br /><br />
+
+.. |indent| replace:: |indent-texinfo| |indent-html|
+
+.. |indent-texinfo| raw:: texinfo
+
+   @* @ @ @ @
+
+.. |indent-html| raw:: html
+
+   <br />&nbsp;&nbsp;&nbsp;&nbsp;
index f673c0a27385803a684540b6ce0078ad453f6f92..480d93817bb627e6afb2c1ef5cc8fe7093355945 100644 (file)
@@ -19,6 +19,7 @@
 #include <stdbool.h>
 
 #include "gmime-filter-reply.h"
+#include "notmuch-client.h"
 
 /**
  * SECTION: gmime-filter-reply
@@ -29,7 +30,7 @@
  **/
 
 
-static void g_mime_filter_reply_class_init (GMimeFilterReplyClass *klass);
+static void g_mime_filter_reply_class_init (GMimeFilterReplyClass *klass, void *class_data);
 static void g_mime_filter_reply_init (GMimeFilterReply *filter, GMimeFilterReplyClass *klass);
 static void g_mime_filter_reply_finalize (GObject *object);
 
@@ -50,16 +51,16 @@ g_mime_filter_reply_get_type (void)
 
        if (!type) {
                static const GTypeInfo info = {
-                       sizeof (GMimeFilterReplyClass),
-                       NULL, /* base_class_init */
-                       NULL, /* base_class_finalize */
-                       (GClassInitFunc) g_mime_filter_reply_class_init,
-                       NULL, /* class_finalize */
-                       NULL, /* class_data */
-                       sizeof (GMimeFilterReply),
-                       0,    /* n_preallocs */
-                       (GInstanceInitFunc) g_mime_filter_reply_init,
-                       NULL    /* value_table */
+                       .class_size = sizeof (GMimeFilterReplyClass),
+                       .base_init = NULL,
+                       .base_finalize = NULL,
+                       .class_init = (GClassInitFunc) g_mime_filter_reply_class_init,
+                       .class_finalize = NULL,
+                       .class_data = NULL,
+                       .instance_size = sizeof (GMimeFilterReply),
+                       .n_preallocs = 0,
+                       .instance_init = (GInstanceInitFunc) g_mime_filter_reply_init,
+                       .value_table = NULL,
                };
 
                type = g_type_register_static (GMIME_TYPE_FILTER, "GMimeFilterReply", &info, (GTypeFlags) 0);
@@ -70,7 +71,7 @@ g_mime_filter_reply_get_type (void)
 
 
 static void
-g_mime_filter_reply_class_init (GMimeFilterReplyClass *klass)
+g_mime_filter_reply_class_init (GMimeFilterReplyClass *klass, unused (void *class_data))
 {
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
        GMimeFilterClass *filter_class = GMIME_FILTER_CLASS (klass);
index 3f694387c36c2cc21c1509881f7596e3b7980b14..efd9da4ca015681d624be82354a06d1f5f0c7855 100644 (file)
@@ -142,7 +142,8 @@ static void filter_reset (GMimeFilter *filter);
 static GMimeFilterClass *parent_class = NULL;
 
 static void
-notmuch_filter_discard_non_term_class_init (NotmuchFilterDiscardNonTermClass *klass)
+notmuch_filter_discard_non_term_class_init (NotmuchFilterDiscardNonTermClass *klass,
+                                           unused (void *class_data))
 {
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
     GMimeFilterClass *filter_class = GMIME_FILTER_CLASS (klass);
@@ -246,16 +247,16 @@ notmuch_filter_discard_non_term_new (GMimeContentType *content_type)
 
     if (!type) {
        static const GTypeInfo info = {
-           sizeof (NotmuchFilterDiscardNonTermClass),
-           NULL, /* base_class_init */
-           NULL, /* base_class_finalize */
-           (GClassInitFunc) notmuch_filter_discard_non_term_class_init,
-           NULL, /* class_finalize */
-           NULL, /* class_data */
-           sizeof (NotmuchFilterDiscardNonTerm),
-           0,    /* n_preallocs */
-           NULL, /* instance_init */
-           NULL  /* value_table */
+           .class_size = sizeof (NotmuchFilterDiscardNonTermClass),
+           .base_init = NULL,
+           .base_finalize = NULL,
+           .class_init = (GClassInitFunc) notmuch_filter_discard_non_term_class_init,
+           .class_finalize = NULL,
+           .class_data = NULL,
+           .instance_size = sizeof (NotmuchFilterDiscardNonTerm),
+           .n_preallocs = 0,
+           .instance_init = NULL,
+           .value_table = NULL,
        };
 
        type = g_type_register_static (GMIME_TYPE_FILTER, "NotmuchFilterDiscardNonTerm", &info, (GTypeFlags) 0);
index 247f6ad71ebf1223b32f1c2b5e226261a309ffc1..24708f3c5947c6819570f493bb249284e40407cc 100644 (file)
@@ -889,10 +889,12 @@ notmuch_query_add_tag_exclude (notmuch_query_t *query, const char *tag);
  *     notmuch_query_t *query;
  *     notmuch_threads_t *threads;
  *     notmuch_thread_t *thread;
+ *     notmuch_status_t stat;
  *
  *     query = notmuch_query_create (database, query_string);
  *
- *     for (threads = notmuch_query_search_threads (query);
+ *     for (stat = notmuch_query_search_threads (query, &threads);
+ *         stat == NOTMUCH_STATUS_SUCCESS &&
  *          notmuch_threads_valid (threads);
  *          notmuch_threads_move_to_next (threads))
  *     {
@@ -1400,6 +1402,8 @@ notmuch_message_get_thread_id (notmuch_message_t *message);
  * If there are no replies to 'message', this function will return
  * NULL. (Note that notmuch_messages_valid will accept that NULL
  * value as legitimate, and simply return FALSE for it.)
+ *
+ * The returned list will be destroyed when the thread is destroyed.
  */
 notmuch_messages_t *
 notmuch_message_get_replies (notmuch_message_t *message);
index 75cf7ecb5068534df678e37fd0a606cd6215739e..d87ec06d83f4989a8d6563fe141fd025c656b4c4 100644 (file)
@@ -369,6 +369,14 @@ add_recipients_from_message (GMimeMessage *reply,
        }
     }
 
+    /* If no recipients were added but we found one of the user's
+     * addresses to use as a from address then the message is from the
+     * user to the user - add the discovered from address to the list
+     * of recipients so that the reply goes back to the user.
+     */
+    if (n == 0 && from_addr)
+       g_mime_message_add_recipient (reply, GMIME_ADDRESS_TYPE_TO, NULL, from_addr);
+
     return from_addr;
 }
 
index c3a3783a4094c952f61b567907b87f658a1381ab..07e9a5dbc8169e3618cea09bc1a7da8389b0040f 100644 (file)
@@ -574,12 +574,18 @@ format_part_text (const void *ctx, sprinter_t *sp, mime_node_t *node,
        g_mime_stream_printf (stream, "Date: %s\n", date_string);
        g_mime_stream_printf (stream, "\fheader}\n");
 
+       if (!params->output_body)
+       {
+           g_mime_stream_printf (stream, "\f%s}\n", part_type);
+           return NOTMUCH_STATUS_SUCCESS;
+       }
        g_mime_stream_printf (stream, "\fbody{\n");
     }
 
     if (leaf) {
        if (g_mime_content_type_is_type (content_type, "text", "*") &&
-           !g_mime_content_type_is_type (content_type, "text", "html"))
+           (params->include_html ||
+            ! g_mime_content_type_is_type (content_type, "text", "html")))
        {
            show_text_part_content (node->part, stream, 0);
        } else {
@@ -1204,15 +1210,19 @@ notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
            fprintf (stderr, "Warning: --body=false is incompatible with --part > 0. Disabling.\n");
            params.output_body = true;
        } else {
-           if (format != NOTMUCH_FORMAT_JSON && format != NOTMUCH_FORMAT_SEXP)
+           if (format != NOTMUCH_FORMAT_TEXT &&
+               format != NOTMUCH_FORMAT_JSON &&
+               format != NOTMUCH_FORMAT_SEXP)
                fprintf (stderr,
-                        "Warning: --body=false only implemented for format=json and format=sexp\n");
+                        "Warning: --body=false only implemented for format=text, format=json and format=sexp\n");
        }
     }
 
     if (params.include_html &&
-        (format != NOTMUCH_FORMAT_JSON && format != NOTMUCH_FORMAT_SEXP)) {
-       fprintf (stderr, "Warning: --include-html only implemented for format=json and format=sexp\n");
+        (format != NOTMUCH_FORMAT_TEXT &&
+        format != NOTMUCH_FORMAT_JSON &&
+        format != NOTMUCH_FORMAT_SEXP)) {
+       fprintf (stderr, "Warning: --include-html only implemented for format=text, format=json and format=sexp\n");
     }
 
     query_string = query_string_from_args (config, argc-opt_index, argv+opt_index);
index 73fe7e24620f923826803dbaa0449e1350cd1716..69080e5e6123d75e3dec3d98e2d0d41c7f72b1a4 100644 (file)
@@ -9,3 +9,4 @@
 /test-results
 /ghost-report
 /tmp.*
+/message-id-parse
index 3eeac1db7920fad67b1c935a13f20b376748d2ee..9ad141cb5ace12793a477a152dbf271958d2672d 100755 (executable)
@@ -190,6 +190,21 @@ Non-text part: application/pgp-signature
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest "--format=text --part=0 --body=false, message header"
+notmuch show --format=text --part=0  --body=false 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+\fmessage{ id:87liy5ap00.fsf@yoom.home.cworth.org depth:0 match:1 excluded:0 filename:${MAIL_DIR}/multipart
+\fheader{
+Carl Worth <cworth@cworth.org> (2001-01-05) (attachment inbox signed unread)
+Subject: Multipart message
+From: Carl Worth <cworth@cworth.org>
+To: cworth@cworth.org
+Date: Fri, 05 Jan 2001 15:43:57 +0000
+\fheader}
+\fmessage}
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
 test_begin_subtest "--format=text --part=1, message body"
 notmuch show --format=text --part=1 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
 cat <<EOF >EXPECTED
@@ -310,6 +325,15 @@ Non-text part: text/html
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest "--format=text --include-html --part=5, rfc822's html part"
+notmuch show --format=text --include-html --part=5 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+\fpart{ ID: 5, Content-type: text/html
+<p>This is an embedded message, with a multipart/alternative part.</p>
+\fpart}
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
 test_begin_subtest "--format=text --part=6, rfc822's text part"
 notmuch show --format=text --part=6 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
 cat <<EOF >EXPECTED
index ebe710f98c55a6d9130449be5c5dc47143bff51e..4db3a95872df60e3fa8e952da5720e7110276339 100755 (executable)
@@ -180,6 +180,7 @@ test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.or
 Subject: Re: This subject is exactly 200 bytes in length. Other than its
  length there is not much of note here. Note that the length of 200 bytes
  includes the Subject: and Re: prefixes with two spaces
+To: test_suite@notmuchmail.org
 In-Reply-To: <${gen_msg_id}>
 References: <${gen_msg_id}>
 
index 134a106365c4618064218c4f5619af5f5a8d54a3..bbeaa2b9c875fae21440af832e649264b9a9c60e 100755 (executable)
@@ -203,6 +203,7 @@ test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.or
 Subject: Re: This subject is exactly 200 bytes in length. Other than its
  length there is not much of note here. Note that the length of 200 bytes
  includes the Subject: and Re: prefixes with two spaces
+To: test_suite@notmuchmail.org
 In-Reply-To: <${gen_msg_id}>
 References: <${gen_msg_id}>
 
index 5935819f4ba4404eb14802d921ff88c3cd2ea1db..cb9e99a58f7d1ded2740f5c8d28af85d13f54ba6 100755 (executable)
@@ -610,7 +610,7 @@ test_emacs "(let ((message-hidden-headers '()))
            (test-output))"
 cat <<EOF >EXPECTED
 From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: 
+To: test_suite@notmuchmail.org
 Subject: Re: Reply within emacs to an html-only message
 In-Reply-To: <${gen_msg_id}>
 Fcc: ${MAIL_DIR}/sent
@@ -623,7 +623,6 @@ EOF
 test_expect_equal_file EXPECTED OUTPUT
 
 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 '()))
@@ -657,7 +656,7 @@ test_emacs "(let ((message-hidden-headers '()))
              (test-output))"
 cat <<EOF >EXPECTED
 From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: 
+To: test_suite@notmuchmail.org
 Subject: Re: Quote MML tags in reply
 In-Reply-To: <test-emacs-mml-quoting@message.id>
 Fcc: ${MAIL_DIR}/sent
index a776ec35043b324efd1c38572ff18683b0afd960..73aa58dee8a8a758974edc0879e43307a4b5c7d8 100755 (executable)
@@ -388,6 +388,7 @@ output=$(notmuch reply --decrypt=true subject:"test encrypted message 002" \
     | notmuch_drop_mail_headers In-Reply-To References)
 expected='From: Notmuch Test Suite <test_suite@notmuchmail.org>
 Subject: Re: test encrypted message 002
+To: test_suite@notmuchmail.org
 
 On 01 Jan 2000 12:00:00 -0000, Notmuch Test Suite <test_suite@notmuchmail.org> wrote:
 > This is another test encrypted message.'
@@ -401,10 +402,10 @@ test_emacs "(let ((message-hidden-headers '())
   (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
+grep -v -e '^In-Reply-To:' -e '^References:' -e '^Fcc:' < OUTPUT > OUTPUT.clean
 cat <<EOF >EXPECTED
 From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: test_suite@notmuchmail.org
 Subject: Re: test encrypted message 002
 --text follows this line--
 <#secure method=pgpmime mode=signencrypt>
diff --git a/test/T720-lib-lifetime.sh b/test/T720-lib-lifetime.sh
new file mode 100755 (executable)
index 0000000..3d94d4d
--- /dev/null
@@ -0,0 +1,83 @@
+#!/usr/bin/env bash
+#
+# Copyright (c) 2018 rhn
+#
+
+
+test_description="Lifetime constraints for library"
+
+. $(dirname "$0")/test-lib.sh || exit 1
+
+add_email_corpus threading
+
+test_begin_subtest "building database"
+test_expect_success "NOTMUCH_NEW"
+
+test_begin_subtest "Message outlives parent Messages from replies"
+
+test_C ${MAIL_DIR} <<'EOF'
+#include <stdio.h>
+#include <stdlib.h>
+#include <notmuch.h>
+int main (int argc, char** argv)
+{
+   notmuch_database_t *db;
+   notmuch_status_t stat;
+   stat = notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_ONLY, &db);
+   if (stat != NOTMUCH_STATUS_SUCCESS) {
+     fprintf (stderr, "error opening database: %d\n", stat);
+     exit (1);
+   }
+
+   notmuch_query_t *query = notmuch_query_create (db, "id:B00-root@example.org");
+   notmuch_threads_t *threads;
+
+   stat = notmuch_query_search_threads (query, &threads);
+   if (stat != NOTMUCH_STATUS_SUCCESS) {
+     fprintf (stderr, "error querying threads: %d\n", stat);
+     exit (1);
+   }
+
+   if (!notmuch_threads_valid (threads)) {
+     fprintf (stderr, "invalid threads");
+     exit (1);
+   }
+
+   notmuch_thread_t *thread = notmuch_threads_get (threads);
+   notmuch_messages_t *messages = notmuch_thread_get_messages (thread);
+
+   if (!notmuch_messages_valid (messages)) {
+     fprintf (stderr, "invalid messages");
+     exit (1);
+   }
+
+   notmuch_message_t *message = notmuch_messages_get (messages);
+   notmuch_messages_t *replies = notmuch_message_get_replies (message);
+   if (!notmuch_messages_valid (replies)) {
+     fprintf (stderr, "invalid replies");
+     exit (1);
+   }
+
+   notmuch_message_t *reply = notmuch_messages_get (replies);
+
+   notmuch_messages_destroy (replies); // the reply should not get destroyed here
+   notmuch_message_destroy (message);
+   notmuch_messages_destroy (messages); // nor here
+
+   const char *mid = notmuch_message_get_message_id (reply); // should not crash when accessing
+   fprintf (stdout, "Reply id: %s\n", mid);
+   notmuch_message_destroy (reply);
+   notmuch_thread_destroy (thread); // this destroys the reply
+   notmuch_threads_destroy (threads);
+   notmuch_query_destroy (query);
+   notmuch_database_destroy (db);
+}
+EOF
+cat <<'EOF' >EXPECTED
+== stdout ==
+Reply id: B01-child@example.org
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_done