From: David Bremner Date: Sat, 14 Jan 2012 00:52:01 +0000 (-0400) Subject: Merge branch 'release' X-Git-Tag: debian/0.12_rc1-1~219 X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=commitdiff_plain;h=0bbfc5ce8be91b881d9542d86aceec7e6a716e86;hp=ffce9b7c25b9b44ec026b67d96e44cae09c99efe Merge branch 'release' Conflicts: notmuch-reply.c notmuch.1 --- diff --git a/Makefile b/Makefile index 2fb2a613..e5e2e3a3 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ all: # List all subdirectories here. Each contains its own Makefile.local -subdirs = compat completion emacs lib util test +subdirs = compat completion emacs lib man util test # We make all targets depend on the Makefiles themselves. global_deps = Makefile Makefile.config Makefile.local \ diff --git a/Makefile.local b/Makefile.local index 97f397ff..d3bf9478 100644 --- a/Makefile.local +++ b/Makefile.local @@ -53,7 +53,7 @@ endif FINAL_LIBNOTMUCH_LDFLAGS = $(LDFLAGS) $(AS_NEEDED_LDFLAGS) $(CONFIGURE_LDFLAGS) .PHONY: all -all: notmuch notmuch-shared notmuch.1.gz +all: notmuch notmuch-shared ifeq ($(MAKECMDGOALS),) ifeq ($(shell cat .first-build-message 2>/dev/null),) @NOTMUCH_FIRST_BUILD=1 $(MAKE) --no-print-directory all @@ -95,8 +95,7 @@ dist: $(TAR_FILE) .PHONY: update-versions -update-versions: - sed -i "s/^.TH NOTMUCH 1.*$$/.TH NOTMUCH 1 ${DATE} \"Notmuch ${VERSION}\"/" notmuch.1 +update-versions: update-man-versions sed -i "s/^__VERSION__[[:blank:]]*=.*$$/__VERSION__ = \'${VERSION}\'/" $(PV_FILE) # We invoke make recursively only to force ordering of our phony @@ -221,14 +220,6 @@ verify-version-python: verify-version-components echo "Please edit version and $(PV_FILE) to have consistent versions." && false) @echo "Good." -.PHONY: verify-version-manpage -verify-version-manpage: verify-version-components - @echo -n "Checking that manual page version is $(VERSION)..." - @[ "$(VERSION)" = $$(sed -n '/^[.]TH NOTMUCH 1/{s/.*"Notmuch //;s/".*//p;}' notmuch.1) ] || \ - (echo "No." && \ - echo "Please edit version and notmuch.1 to have consistent versions." && false) - @echo "Good." - .PHONY: verify-version-components verify-version-components: @echo -n "Checking that $(VERSION) consists only of digits and periods..." @@ -315,6 +306,7 @@ notmuch_client_srcs = \ notmuch-time.c \ query-string.c \ show-message.c \ + mime-node.c \ json.c notmuch_client_modules = $(notmuch_client_srcs:.c=.o) @@ -325,13 +317,8 @@ notmuch: $(notmuch_client_modules) lib/libnotmuch.a util/libutil.a notmuch-shared: $(notmuch_client_modules) lib/$(LINKER_NAME) $(call quiet,$(FINAL_NOTMUCH_LINKER) $(CFLAGS)) $(notmuch_client_modules) $(FINAL_NOTMUCH_LDFLAGS) -o $@ -notmuch.1.gz: notmuch.1 - gzip --stdout $^ > $@ - .PHONY: install -install: all notmuch.1.gz - mkdir -p "$(DESTDIR)$(mandir)/man1" - install -m0644 notmuch.1.gz "$(DESTDIR)$(mandir)/man1/" +install: all install-man mkdir -p "$(DESTDIR)$(prefix)/bin/" install notmuch-shared "$(DESTDIR)$(prefix)/bin/notmuch" ifeq ($(MAKECMDGOALS), install) @@ -362,4 +349,4 @@ install-desktop: desktop-file-install --mode 0644 --dir "$(DESTDIR)$(desktop_dir)" notmuch.desktop SRCS := $(SRCS) $(notmuch_client_srcs) -CLEAN := $(CLEAN) notmuch notmuch-shared $(notmuch_client_modules) notmuch.elc notmuch.1.gz +CLEAN := $(CLEAN) notmuch notmuch-shared $(notmuch_client_modules) notmuch.elc diff --git a/bindings/python/notmuch.py b/bindings/python/notmuch.py index 8d118595..3ff53ec8 100755 --- a/bindings/python/notmuch.py +++ b/bindings/python/notmuch.py @@ -17,7 +17,12 @@ import stat import email from notmuch import Database, Query, NotmuchError, STATUS -from ConfigParser import SafeConfigParser +try: + # python3.x + from configparser import SafeConfigParser +except ImportError: + # python2.x + from ConfigParser import SafeConfigParser from cStringIO import StringIO PREFIX = re.compile('(\w+):(.*$)') diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py index 7923f768..24da8e99 100644 --- a/bindings/python/notmuch/database.py +++ b/bindings/python/notmuch/database.py @@ -430,7 +430,7 @@ class Database(object): removed. """ self._assert_db_is_initialized() - return self._remove_message(self._db, filename) + return self._remove_message(self._db, _str(filename)) def find_message(self, msgid): """Returns a :class:`Message` as identified by its message ID @@ -543,7 +543,13 @@ class Database(object): """ Reads a user's notmuch config and returns his db location Throws a NotmuchError if it cannot find it""" - from ConfigParser import SafeConfigParser + try: + # python3.x + from configparser import SafeConfigParser + except ImportError: + # python2.x + from ConfigParser import SafeConfigParser + config = SafeConfigParser() conf_f = os.getenv('NOTMUCH_CONFIG', os.path.expanduser('~/.notmuch-config')) @@ -919,7 +925,7 @@ class Filenames(object): _move_to_next.argtypes = [NotmuchFilenamesP] _move_to_next.restype = None - def next(self): + def __next__(self): if self._files_p is None: raise NotmuchError(STATUS.NOT_INITIALIZED) @@ -927,9 +933,10 @@ class Filenames(object): self._files_p = None raise StopIteration - file = Filenames._get(self._files_p) + file_ = Filenames._get(self._files_p) self._move_to_next(self._files_p) - return file + return file_.decode('utf-8', 'ignore') + next = __next__ # python2.x iterator protocol compatibility def __len__(self): """len(:class:`Filenames`) returns the number of contained files diff --git a/bindings/python/notmuch/filename.py b/bindings/python/notmuch/filename.py index a7cd7e63..51dae202 100644 --- a/bindings/python/notmuch/filename.py +++ b/bindings/python/notmuch/filename.py @@ -18,10 +18,10 @@ Copyright 2010 Sebastian Spaeth ' """ from ctypes import c_char_p from notmuch.globals import (nmlib, STATUS, NotmuchError, - NotmuchFilenamesP, NotmuchMessageP) + NotmuchFilenamesP, NotmuchMessageP, _str, Python3StringMixIn) -class Filenames(object): +class Filenames(Python3StringMixIn): """Represents a list of filenames as returned by notmuch This object contains the Filenames iterator. The main function is @@ -93,14 +93,11 @@ class Filenames(object): raise NotmuchError(STATUS.NOT_INITIALIZED) while self._valid(self._files): - yield Filenames._get(self._files) + yield Filenames._get(self._files).decode('utf-8', 'ignore') self._move_to_next(self._files) self._files = None - def __str__(self): - return unicode(self).encode('utf-8') - def __unicode__(self): """Represent Filenames() as newline-separated list of full paths diff --git a/bindings/python/notmuch/globals.py b/bindings/python/notmuch/globals.py index 54a49b2d..32ed9ae4 100644 --- a/bindings/python/notmuch/globals.py +++ b/bindings/python/notmuch/globals.py @@ -16,7 +16,7 @@ along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth ' """ - +import sys from ctypes import CDLL, c_char_p, c_int, Structure, POINTER #----------------------------------------------------------------------------- @@ -27,6 +27,16 @@ except: raise ImportError("Could not find shared 'notmuch' library.") +if sys.version_info[0] == 2: + class Python3StringMixIn(object): + def __str__(self): + return unicode(self).encode('utf-8') +else: + class Python3StringMixIn(object): + def __str__(self): + return self.__unicode__() + + class Enum(object): """Provides ENUMS as "code=Enum(['a','b','c'])" where code.a=0 etc...""" def __init__(self, names): @@ -51,7 +61,7 @@ class Status(Enum): """Get a (unicode) string representation of a notmuch_status_t value.""" # define strings for custom error messages if status == STATUS.NOT_INITIALIZED: - return u"Operation on uninitialized object impossible." + return "Operation on uninitialized object impossible." return unicode(Status._status2str(status)) STATUS = Status(['SUCCESS', @@ -89,7 +99,7 @@ argument to receive a human readable string""" STATUS.__name__ = 'STATUS' -class NotmuchError(Exception): +class NotmuchError(Exception, Python3StringMixIn): """Is initiated with a (notmuch.STATUS[, message=None]). It will not return an instance of the class NotmuchError, but a derived instance of a more specific Error Message, e.g. OutOfMemoryError. Each status @@ -133,16 +143,13 @@ class NotmuchError(Exception): self.status = status self.message = message - def __str__(self): - return unicode(self).encode('utf-8') - def __unicode__(self): if self.message is not None: return self.message elif self.status is not None: return STATUS.status2str(self.status) else: - return u'Unknown error' + return 'Unknown error' # List of Subclassed exceptions that correspond to STATUS values and are diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py index ce8e7181..d40a575d 100644 --- a/bindings/python/notmuch/message.py +++ b/bindings/python/notmuch/message.py @@ -21,7 +21,8 @@ Copyright 2010 Sebastian Spaeth ' from ctypes import c_char_p, c_long, c_uint, c_int from datetime import date -from notmuch.globals import (nmlib, STATUS, NotmuchError, Enum, _str, +from notmuch.globals import ( + nmlib, STATUS, NotmuchError, Enum, _str, Python3StringMixIn, NotmuchTagsP, NotmuchMessagesP, NotmuchMessageP, NotmuchFilenamesP) from notmuch.tag import Tags from notmuch.filename import Filenames @@ -158,7 +159,7 @@ class Messages(object): _move_to_next.argtypes = [NotmuchMessagesP] _move_to_next.restype = None - def next(self): + def __next__(self): if self._msgs is None: raise NotmuchError(STATUS.NOT_INITIALIZED) @@ -169,6 +170,7 @@ class Messages(object): msg = Message(Messages._get(self._msgs), self) self._move_to_next(self._msgs) return msg + next = __next__ # python2.x iterator protocol compatibility def __nonzero__(self): """ @@ -186,14 +188,17 @@ class Messages(object): if self._msgs is not None: self._destroy(self._msgs) - def print_messages(self, format, indent=0, entire_thread=False): - """Outputs messages as needed for 'notmuch show' to sys.stdout + def format_messages(self, format, indent=0, entire_thread=False): + """Formats messages as needed for 'notmuch show'. :param format: A string of either 'text' or 'json'. :param indent: A number indicating the reply depth of these messages. :param entire_thread: A bool, indicating whether we want to output whole threads or only the matching messages. + :return: a list of lines """ + result = list() + if format.lower() == "text": set_start = "" set_end = "" @@ -207,38 +212,61 @@ class Messages(object): first_set = True - sys.stdout.write(set_start) + result.append(set_start) # iterate through all toplevel messages in this thread for msg in self: # if not msg: # break if not first_set: - sys.stdout.write(set_sep) + result.append(set_sep) first_set = False - sys.stdout.write(set_start) + result.append(set_start) match = msg.is_match() next_indent = indent if (match or entire_thread): if format.lower() == "text": - sys.stdout.write(msg.format_message_as_text(indent)) + result.append(msg.format_message_as_text(indent)) else: - sys.stdout.write(msg.format_message_as_json(indent)) + result.append(msg.format_message_as_json(indent)) next_indent = indent + 1 # get replies and print them also out (if there are any) - replies = msg.get_replies() - if not replies is None: - sys.stdout.write(set_sep) - replies.print_messages(format, next_indent, entire_thread) + replies = msg.get_replies().format_messages(format, next_indent, entire_thread) + if replies: + result.append(set_sep) + result.extend(replies) + + result.append(set_end) + result.append(set_end) + + return result + + def print_messages(self, format, indent=0, entire_thread=False, handle=sys.stdout): + """Outputs messages as needed for 'notmuch show' to a file like object. + + :param format: A string of either 'text' or 'json'. + :param handle: A file like object to print to (default is sys.stdout). + :param indent: A number indicating the reply depth of these messages. + :param entire_thread: A bool, indicating whether we want to output + whole threads or only the matching messages. + """ + handle.write(''.join(self.format_messages(format, indent, entire_thread))) + + +class EmptyMessagesResult(Messages): + def __init__(self, parent): + self._msgs = None + self._parent = parent - sys.stdout.write(set_end) - sys.stdout.write(set_end) + def __next__(self): + raise StopIteration() + next = __next__ -class Message(object): +class Message(Python3StringMixIn): """Represents a single Email message Technically, this wraps the underlying *notmuch_message_t* @@ -336,7 +364,7 @@ class Message(object): """ if self._msg is None: raise NotmuchError(STATUS.NOT_INITIALIZED) - return Message._get_message_id(self._msg) + return Message._get_message_id(self._msg).decode('utf-8', 'ignore') def get_thread_id(self): """Returns the thread ID @@ -354,7 +382,7 @@ class Message(object): if self._msg is None: raise NotmuchError(STATUS.NOT_INITIALIZED) - return Message._get_thread_id(self._msg) + return Message._get_thread_id(self._msg).decode('utf-8', 'ignore') def get_replies(self): """Gets all direct replies to this message as :class:`Messages` @@ -368,10 +396,9 @@ class Message(object): number of subsequent calls to :meth:`get_replies`). If this message was obtained through some non-thread means, (such as by a call to :meth:`Query.search_messages`), then this function will return - `None`. + an empty Messages iterator. - :returns: :class:`Messages` or `None` if there are no replies to - this message. + :returns: :class:`Messages`. :exception: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if the message is not initialized. """ @@ -381,7 +408,7 @@ class Message(object): msgs_p = Message._get_replies(self._msg) if msgs_p is None: - return None + return EmptyMessagesResult(self) return Messages(msgs_p, self) @@ -424,10 +451,10 @@ class Message(object): raise NotmuchError(STATUS.NOT_INITIALIZED) #Returns NULL if any error occurs. - header = Message._get_header(self._msg, header) + header = Message._get_header(self._msg, _str(header)) if header == None: raise NotmuchError(STATUS.NULL_POINTER) - return header.decode('UTF-8', errors='ignore') + return header.decode('UTF-8', 'ignore') def get_filename(self): """Returns the file path of the message file @@ -438,7 +465,7 @@ class Message(object): """ if self._msg is None: raise NotmuchError(STATUS.NOT_INITIALIZED) - return Message._get_filename(self._msg) + return Message._get_filename(self._msg).decode('utf-8', 'ignore') def get_filenames(self): """Get all filenames for the email corresponding to 'message' @@ -795,9 +822,6 @@ class Message(object): """Represent a Message() object by str()""" return self.__str__() - def __str__(self): - return unicode(self).encode('utf-8') - def __unicode__(self): format = "%s (%s) (%s)" return format % (self.get_header('from'), diff --git a/bindings/python/notmuch/tag.py b/bindings/python/notmuch/tag.py index 2fb7d328..ceb72441 100644 --- a/bindings/python/notmuch/tag.py +++ b/bindings/python/notmuch/tag.py @@ -17,10 +17,10 @@ along with notmuch. If not, see . Copyright 2010 Sebastian Spaeth ' """ from ctypes import c_char_p -from notmuch.globals import nmlib, STATUS, NotmuchError, NotmuchTagsP +from notmuch.globals import nmlib, STATUS, NotmuchError, NotmuchTagsP, _str, Python3StringMixIn -class Tags(object): +class Tags(Python3StringMixIn): """Represents a list of notmuch tags This object provides an iterator over a list of notmuch tags (which @@ -89,7 +89,7 @@ class Tags(object): _move_to_next.argtypes = [NotmuchTagsP] _move_to_next.restype = None - def next(self): + def __next__(self): if self._tags is None: raise NotmuchError(STATUS.NOT_INITIALIZED) if not self._valid(self._tags): @@ -98,6 +98,7 @@ class Tags(object): tag = Tags._get(self._tags).decode('UTF-8') self._move_to_next(self._tags) return tag + next = __next__ # python2.x iterator protocol compatibility def __nonzero__(self): """Implement bool(Tags) check that can be repeatedly used @@ -110,9 +111,6 @@ class Tags(object): left.""" return self._valid(self._tags) > 0 - def __str__(self): - return unicode(self).encode('utf-8') - def __unicode__(self): """string representation of :class:`Tags`: a space separated list of tags diff --git a/bindings/python/notmuch/thread.py b/bindings/python/notmuch/thread.py index 5058846d..e81ff1bd 100644 --- a/bindings/python/notmuch/thread.py +++ b/bindings/python/notmuch/thread.py @@ -20,13 +20,13 @@ Copyright 2010 Sebastian Spaeth ' from ctypes import c_char_p, c_long, c_int from notmuch.globals import (nmlib, STATUS, NotmuchError, NotmuchThreadP, NotmuchThreadsP, NotmuchMessagesP, - NotmuchTagsP,) + NotmuchTagsP, Python3StringMixIn) from notmuch.message import Messages from notmuch.tag import Tags from datetime import date -class Threads(object): +class Threads(Python3StringMixIn): """Represents a list of notmuch threads This object provides an iterator over a list of notmuch threads @@ -116,7 +116,7 @@ class Threads(object): _move_to_next.argtypes = [NotmuchThreadsP] _move_to_next.restype = None - def next(self): + def __next__(self): if self._threads is None: raise NotmuchError(STATUS.NOT_INITIALIZED) @@ -127,6 +127,7 @@ class Threads(object): thread = Thread(Threads._get(self._threads), self) self._move_to_next(self._threads) return thread + next = __next__ # python2.x iterator protocol compatibility def __len__(self): """len(:class:`Threads`) returns the number of contained Threads @@ -245,7 +246,7 @@ class Thread(object): """ if self._thread is None: raise NotmuchError(STATUS.NOT_INITIALIZED) - return Thread._get_thread_id(self._thread) + return Thread._get_thread_id(self._thread).decode('utf-8', 'ignore') _get_total_messages = nmlib.notmuch_thread_get_total_messages _get_total_messages.argtypes = [NotmuchThreadP] @@ -325,7 +326,7 @@ class Thread(object): authors = Thread._get_authors(self._thread) if authors is None: return None - return authors.decode('UTF-8', errors='ignore') + return authors.decode('UTF-8', 'ignore') def get_subject(self): """Returns the Subject of 'thread' @@ -338,7 +339,7 @@ class Thread(object): subject = Thread._get_subject(self._thread) if subject is None: return None - return subject.decode('UTF-8', errors='ignore') + return subject.decode('UTF-8', 'ignore') def get_newest_date(self): """Returns time_t of the newest message date @@ -391,9 +392,6 @@ class Thread(object): raise NotmuchError(STATUS.NULL_POINTER) return Tags(tags_p, self) - def __str__(self): - return unicode(self).encode('utf-8') - def __unicode__(self): frm = "thread:%s %12s [%d/%d] %s; %s (%s)" diff --git a/bindings/python/setup.py b/bindings/python/setup.py index 286fd196..2e58dab1 100644 --- a/bindings/python/setup.py +++ b/bindings/python/setup.py @@ -7,7 +7,7 @@ from distutils.core import setup # get the notmuch version number without importing the notmuch module version_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'notmuch', 'version.py') -execfile(version_file) +exec(compile(open(version_file).read(), version_file, 'exec')) assert __VERSION__, 'Failed to read the notmuch binding version number' setup(name='notmuch', diff --git a/debian/notmuch.install b/debian/notmuch.install index fff498d2..86e891d4 100644 --- a/debian/notmuch.install +++ b/debian/notmuch.install @@ -1,3 +1,3 @@ usr/bin -usr/share/man/man1 +usr/share/man etc/bash_completion.d diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el index 333d4c1e..02017ce5 100644 --- a/emacs/notmuch-hello.el +++ b/emacs/notmuch-hello.el @@ -299,15 +299,17 @@ should be. Returns a cons cell `(tags-per-line width)'." :notify #'notmuch-hello-widget-search :notmuch-search-terms query formatted-name) - ;; Insert enough space to consume the rest of the - ;; column. Because the button for the name is `(1+ - ;; (length name))' long (due to the trailing space) we - ;; can just insert `(- widest (length name))' spaces - - ;; the column separator is included in the button if - ;; `(equal widest (length name)'. - (widget-insert (make-string (max 1 - (- widest (length name))) - ? )))) + (unless (eq (% count tags-per-line) (1- tags-per-line)) + ;; If this is not the last tag on the line, insert + ;; enough space to consume the rest of the column. + ;; Because the button for the name is `(1+ (length + ;; name))' long (due to the trailing space) we can + ;; just insert `(- widest (length name))' spaces - the + ;; column separator is included in the button if + ;; `(equal widest (length name)'. + (widget-insert (make-string (max 1 + (- widest (length name))) + ? ))))) (setq count (1+ count)) (if (eq (% count tags-per-line) 0) (widget-insert "\n"))) diff --git a/emacs/notmuch-mua.el b/emacs/notmuch-mua.el index 7114e48a..32e2e30b 100644 --- a/emacs/notmuch-mua.el +++ b/emacs/notmuch-mua.el @@ -108,7 +108,8 @@ list." (if (re-search-backward message-signature-separator nil t) (forward-line -1) (goto-char (point-max))) - (insert body)) + (insert body) + (push-mark)) (set-buffer-modified-p nil) (message-goto-body)) diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el index 82d11c92..2806879e 100644 --- a/emacs/notmuch-show.el +++ b/emacs/notmuch-show.el @@ -75,7 +75,10 @@ any given message." :group 'notmuch :type 'hook) -(defcustom notmuch-show-insert-text/plain-hook '(notmuch-wash-excerpt-citations) +(defcustom notmuch-show-insert-text/plain-hook '(notmuch-wash-wrap-long-lines + notmuch-wash-tidy-citations + notmuch-wash-elide-blank-lines + notmuch-wash-excerpt-citations) "Functions used to improve the display of text/plain parts." :group 'notmuch :type 'hook @@ -585,6 +588,10 @@ current buffer, if possible." nil)) nil)))) +;; Handler for wash generated inline patch fake parts. +(defun notmuch-show-insert-part-inline-patch-fake-part (msg part content-type nth depth declared-type) + (notmuch-show-insert-part-*/* msg part "text/x-diff" nth depth "inline patch")) + (defun notmuch-show-insert-part-*/* (msg part content-type nth depth declared-type) ;; This handler _must_ succeed - it is the handler of last resort. (notmuch-show-insert-part-header nth content-type declared-type (plist-get part :filename)) diff --git a/emacs/notmuch-wash.el b/emacs/notmuch-wash.el index 1f420b25..5c1e8300 100644 --- a/emacs/notmuch-wash.el +++ b/emacs/notmuch-wash.el @@ -290,6 +290,44 @@ When doing so, maintaining citation leaders in the wrapped text." (defvar diff-file-header-re) ; From `diff-mode.el'. +(defun notmuch-wash-subject-to-filename (subject &optional maxlen) + "Convert a mail SUBJECT into a filename. + +The resulting filename is similar to the names generated by \"git +format-patch\", without the leading patch sequence number +\"0001-\" and \".patch\" extension. Any leading \"[PREFIX]\" +style strings are removed prior to conversion. + +Optional argument MAXLEN is the maximum length of the resulting +filename, before trimming any trailing . and - characters." + (let* ((s (replace-regexp-in-string "^ *\\(\\[[^]]*\\] *\\)*" "" subject)) + (s (replace-regexp-in-string "[^A-Za-z0-9._]+" "-" s)) + (s (replace-regexp-in-string "\\.+" "." s)) + (s (if maxlen (substring s 0 (min (length s) maxlen)) s)) + (s (replace-regexp-in-string "[.-]*$" "" s))) + s)) + +(defun notmuch-wash-subject-to-patch-sequence-number (subject) + "Convert a patch mail SUBJECT into a patch sequence number. + +Return the patch sequence number N from the last \"[PATCH N/M]\" +style prefix in SUBJECT, or nil if such a prefix can't be found." + (when (string-match + "^ *\\(\\[[^]]*\\] *\\)*\\[[^]]*?\\([0-9]+\\)/[0-9]+[^]]*\\].*" + subject) + (string-to-number (substring subject (match-beginning 2) (match-end 2))))) + +(defun notmuch-wash-subject-to-patch-filename (subject) + "Convert a patch mail SUBJECT into a filename. + +The resulting filename is similar to the names generated by \"git +format-patch\". If the patch mail was generated and sent using +\"git format-patch/send-email\", this should re-create the +original filename the sender had." + (format "%04d-%s.patch" + (or (notmuch-wash-subject-to-patch-sequence-number subject) 1) + (notmuch-wash-subject-to-filename subject 52))) + (defun notmuch-wash-convert-inline-patch-to-part (msg depth) "Convert an inline patch into a fake 'text/x-diff' attachment. @@ -313,10 +351,13 @@ for error." (setq patch-end (match-beginning 0))) (save-restriction (narrow-to-region patch-start patch-end) - (setq part (plist-put part :content-type "text/x-diff")) + (setq part (plist-put part :content-type "inline-patch-fake-part")) (setq part (plist-put part :content (buffer-string))) (setq part (plist-put part :id -1)) - (setq part (plist-put part :filename "inline patch")) + (setq part (plist-put part :filename + (notmuch-wash-subject-to-patch-filename + (plist-get + (plist-get msg :headers) :Subject)))) (delete-region (point-min) (point-max)) (notmuch-show-insert-bodypart nil part depth)))))) diff --git a/emacs/notmuch.el b/emacs/notmuch.el index fde23779..1e617752 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -164,16 +164,23 @@ For a mouse binding, return nil." "\t" (notmuch-documentation-first-line action)))))) -(defalias 'notmuch-substitute-one-command-key - (apply-partially 'notmuch-substitute-one-command-key-with-prefix nil)) +(defun notmuch-substitute-command-keys-one (key) + ;; A `keymap' key indicates inheritance from a parent keymap - the + ;; inherited mappings follow, so there is nothing to print for + ;; `keymap' itself. + (when (not (eq key 'keymap)) + (notmuch-substitute-one-command-key-with-prefix nil key))) (defun notmuch-substitute-command-keys (doc) "Like `substitute-command-keys' but with documentation, not function names." (let ((beg 0)) (while (string-match "\\\\{\\([^}[:space:]]*\\)}" doc beg) - (let ((map (substring doc (match-beginning 1) (match-end 1)))) - (setq doc (replace-match (mapconcat 'notmuch-substitute-one-command-key - (cdr (symbol-value (intern map))) "\n") 1 1 doc))) + (let* ((keymap-name (substring doc (match-beginning 1) (match-end 1))) + (keymap (symbol-value (intern keymap-name)))) + (setq doc (replace-match + (mapconcat #'notmuch-substitute-command-keys-one + (cdr keymap) "\n") + 1 1 doc))) (setq beg (match-end 0))) doc)) @@ -438,7 +445,7 @@ Complete list of currently available key bindings: "*") 32 nil nil t)) crypto-switch) - (error "End of search results")))) + (message "End of search results.")))) (defun notmuch-search-reply-to-thread (&optional prompt-for-sender) "Begin composing a reply to the entire current thread in a new buffer." diff --git a/lib/index.cc b/lib/index.cc index e8e9922b..d8f8b2bf 100644 --- a/lib/index.cc +++ b/lib/index.cc @@ -339,6 +339,10 @@ _index_mime_part (notmuch_message_t *message, if (i > 1) fprintf (stderr, "Warning: Unexpected extra parts of multipart/signed. Indexing anyway.\n"); } + if (GMIME_IS_MULTIPART_ENCRYPTED (multipart)) { + /* Don't index encrypted parts. */ + continue; + } _index_mime_part (message, g_mime_multipart_get_part (multipart, i)); } diff --git a/lib/messages.c b/lib/messages.c index 7bcd1abf..11218648 100644 --- a/lib/messages.c +++ b/lib/messages.c @@ -127,8 +127,10 @@ notmuch_messages_get (notmuch_messages_t *messages) void notmuch_messages_move_to_next (notmuch_messages_t *messages) { - if (! messages->is_of_list_type) - return _notmuch_mset_messages_move_to_next (messages); + if (! messages->is_of_list_type) { + _notmuch_mset_messages_move_to_next (messages); + return; + } if (messages->iterator == NULL) return; diff --git a/man/Makefile b/man/Makefile new file mode 100644 index 00000000..fa25832e --- /dev/null +++ b/man/Makefile @@ -0,0 +1,5 @@ +all: + $(MAKE) -C .. all + +.DEFAULT: + $(MAKE) -C .. $@ diff --git a/man/Makefile.local b/man/Makefile.local new file mode 100644 index 00000000..d43a949c --- /dev/null +++ b/man/Makefile.local @@ -0,0 +1,60 @@ +# -*- Makefile -*- + +dir := man + +# this variable seems to be needed to prevent lazy evaluation causing +# problems with $(dir) changing values. +MAIN_PAGE := $(dir)/man1/notmuch.1 + +MAN1 := \ + $(MAIN_PAGE) \ + $(dir)/man1/notmuch-config.1 \ + $(dir)/man1/notmuch-count.1 \ + $(dir)/man1/notmuch-dump.1 \ + $(dir)/man1/notmuch-restore.1 \ + $(dir)/man1/notmuch-new.1 \ + $(dir)/man1/notmuch-reply.1 \ + $(dir)/man1/notmuch-search.1 \ + $(dir)/man1/notmuch-show.1 \ + $(dir)/man1/notmuch-tag.1 + +MAN5 := $(dir)/man5/notmuch-hooks.5 +MAN7 := $(dir)/man7/notmuch-search-terms.7 + +MAN1_GZ := $(addsuffix .gz,$(MAN1)) +MAN5_GZ := $(addsuffix .gz,$(MAN5)) +MAN7_GZ := $(addsuffix .gz,$(MAN7)) + +MAN_SOURCE := $(MAN1) $(MAN5) $(MAN7) +MAN_BACKUP := $(addsuffix .bak,$(MAN_SOURCE)) +COMPRESSED_MAN := $(MAN1_GZ) $(MAN5_GZ) $(MAN7_GZ) + +%.gz: % + gzip --stdout $^ > $@ + +.PHONY: install-man update-man-versions verify-version-manpage + +install-man: $(COMPRESSED_MAN) + mkdir -p "$(DESTDIR)$(mandir)/man1" + mkdir -p "$(DESTDIR)$(mandir)/man5" + mkdir -p "$(DESTDIR)$(mandir)/man7" + install -m0644 $(MAN1_GZ) $(DESTDIR)/$(mandir)/man1 + install -m0644 $(MAN5_GZ) $(DESTDIR)/$(mandir)/man5 + install -m0644 $(MAN7_GZ) $(DESTDIR)/$(mandir)/man7 + cd $(DESTDIR)/$(mandir)/man1 && ln -sf notmuch.1.gz notmuch-setup.1.gz + +verify-version-manpage: verify-version-components + @echo -n "Checking that manual page version is $(VERSION)..." + @[ "$(VERSION)" = $$(sed -n '/^[.]TH NOTMUCH 1/{s/.*"Notmuch //;s/".*//p;}' $(MAIN_PAGE)) ] || \ + (echo "No." && \ + echo "Please edit version and notmuch.1 to have consistent versions." && false) + @echo "Good." + +update-man-versions: $(MAN_SOURCE) + for file in $(MAN_SOURCE); do \ + cp $$file $$file.bak ; \ + sed "s/^.TH NOTMUCH\([^[:blank:]]*\) \([1-9]\) .*$$/.TH NOTMUCH\1 \2 ${DATE} \"Notmuch ${VERSION}\"/" \ + < $$file.bak > $$file; \ + done + +CLEAN := $(CLEAN) $(COMPRESSED_MAN) $(MAN_BACKUP) diff --git a/man/man1/notmuch-config.1 b/man/man1/notmuch-config.1 new file mode 100644 index 00000000..cb3234ff --- /dev/null +++ b/man/man1/notmuch-config.1 @@ -0,0 +1,54 @@ +.TH NOTMUCH-CONFIG 1 2011-12-04 "Notmuch 0.10.2" +.SH NAME +notmuch-config \- Output a single part of a multipart MIME message. +.SH SYNOPSIS + +.B notmuch config get +.RI "<" section "> . <" item ">" + +.B notmuch config set +.RI "<" section "> . <" item "> [" value "]" + +.SH DESCRIPTION + +The +.B config +command can be used to get or set settings in the notmuch +configuration file. + +.SS GET + +The value of the specified configuration item is printed to stdout. If +the item has multiple values, each value is separated by a newline +character. + +Available configuration items include at least + + database.path + + user.name + + user.primary_email + + user.other_email + + new.tags + +.SS SET + +The specified configuration item is set to the given value. To +specify a multiple-value item, provide each value as a separate +command-line argument. + +If no values are provided, the specified configuration item will be +removed from the configuration file. +.RE + +.SH SEE ALSO + +\fBnotmuch\fR(1), \fBnotmuch-count\fR(1), +\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5), \fBnotmuch-new\fR(1), +\fBnotmuch-part\fR(1), \fBnotmuch-reply\fR(1), +\fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1), +\fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1), +\fBnotmuch-tag\fR(1) diff --git a/man/man1/notmuch-count.1 b/man/man1/notmuch-count.1 new file mode 100644 index 00000000..25fe3292 --- /dev/null +++ b/man/man1/notmuch-count.1 @@ -0,0 +1,50 @@ +.TH NOTMUCH-COUNT 1 2011-12-04 "Notmuch 0.10.2" +.SH NAME +notmuch-count \- Count messages matching the given search terms. +.SH SYNOPSIS + +.B notmuch count +.RI [ options "... ] <" search-term ">..." + +.SH DESCRIPTION + +Count messages matching the search terms. + +The number of matching messages (or threads) is output to stdout. + +With no search terms, a count of all messages (or threads) in the database will +be displayed. + +See \fBnotmuch-search-terms\fR(7) +for details of the supported syntax for . + +Supported options for +.B count +include +.RS 4 +.TP 4 +.B \-\-output=(messages|threads) + +.RS 4 +.TP 4 +.B messages + +Output the number of matching messages. This is the default. +.RE +.RS 4 +.TP 4 +.B threads + +Output the number of matching threads. +.RE +.RE +.RE +.RE + +.SH SEE ALSO + +\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-dump\fR(1), +\fBnotmuch-hooks\fR(5), \fBnotmuch-new\fR(1), \fBnotmuch-part\fR(1), +\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1), +\fBnotmuch-search\fR(1), \fBnotmuch-search-terms\fR(7), +\fBnotmuch-show\fR(1), \fBnotmuch-tag\fR(1) diff --git a/man/man1/notmuch-dump.1 b/man/man1/notmuch-dump.1 new file mode 100644 index 00000000..9ccf35d5 --- /dev/null +++ b/man/man1/notmuch-dump.1 @@ -0,0 +1,37 @@ +.TH NOTMUCH-DUMP 1 2011-12-04 "Notmuch 0.10.2" +.SH NAME +notmuch-dump \- Creates a plain-text dump of the tags of each message. + +.SH SYNOPSIS + +.B "notmuch dump" +.RI "[ <" filename "> ] [--]" +.RI "[ <" search-term ">...]" + +.SH DESCRIPTION + +Dump tags for messages matching the given search terms. + +Output is to the given filename, if any, or to stdout. Note that +using the filename argument is deprecated. + +These tags are the only data in the notmuch database that can't be +recreated from the messages themselves. The output of notmuch dump is +therefore the only critical thing to backup (and much more friendly to +incremental backup than the native database files.) + +With no search terms, a dump of all messages in the database will be +generated. A "--" argument instructs notmuch that the +remaining arguments are search terms. + +See \fBnotmuch-search-terms\fR(7) +for details of the supported syntax for . + +.RE +.SH SEE ALSO + +\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1), +\fBnotmuch-hooks\fR(5), \fBnotmuch-new\fR(1), \fBnotmuch-part\fR(1), +\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1), +\fBnotmuch-search\fR(1), \fBnotmuch-search-terms\fR(7), +\fBnotmuch-show\fR(1), \fBnotmuch-tag\fR(1) diff --git a/man/man1/notmuch-new.1 b/man/man1/notmuch-new.1 new file mode 100644 index 00000000..77d47764 --- /dev/null +++ b/man/man1/notmuch-new.1 @@ -0,0 +1,65 @@ +.TH NOTMUCH-NEW 1 2011-12-04 "Notmuch 0.10.2" +.SH NAME +notmuch-new \- Incorporate new mail into the notmuch database. +.SH SYNOPSIS + +.B notmuch new +.RB "[" --no-hooks "]" + +.SH DESCRIPTION + +Find and import any new messages to the database. + +The +.B new +command scans all sub-directories of the database, performing +full-text indexing on new messages that are found. Each new message +will automatically be tagged with both the +.BR inbox " and " unread +tags. + +You should run +.B "notmuch new" +once after first running +.B "notmuch setup" +to create the initial database. The first run may take a long time if +you have a significant amount of mail (several hundred thousand +messages or more). Subsequently, you should run +.B "notmuch new" +whenever new mail is delivered and you wish to incorporate it into the +database. These subsequent runs will be much quicker than the initial +run. + +Invoking +.B notmuch +with no command argument will run +.B new +if +.B "notmuch setup" +has previously been completed, but +.B "notmuch new" +has not previously been run. + + +The +.B new +command supports hooks. See \fBnotmuch-hooks(5)\fR +for more details on hooks. + +Supported options for +.B new +include +.RS 4 +.TP 4 +.BR \-\-no\-hooks + +Prevents hooks from being run. +.RE +.RE +.SH SEE ALSO + +\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1), +\fBnotmuch-dump\fR(5), \fBnotmuch-hooks\fR(5), \fBnotmuch-part\fR(1), +\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1), +\fBnotmuch-search\fR(1), \fBnotmuch-search-terms\fR(7), +\fBnotmuch-show\fR(1), \fBnotmuch-tag\fR(1) diff --git a/man/man1/notmuch-reply.1 b/man/man1/notmuch-reply.1 new file mode 100644 index 00000000..db464d80 --- /dev/null +++ b/man/man1/notmuch-reply.1 @@ -0,0 +1,68 @@ +.TH NOTMUCH-REPLY 1 2011-12-04 "Notmuch 0.10.2" +.SH NAME +notmuch-reply \- Constructs a reply template for a set of messages. + +.SH SYNOPSIS + +.B notmuch reply +.RI "[" options "...] <" search-term ">..." + +.SH DESCRIPTION + +Constructs a reply template for a set of messages. + +To make replying to email easier, +.B notmuch reply +takes an existing set of messages and constructs a suitable mail +template. The Reply-to header (if any, otherwise From:) is used for +the To: address. Vales from the To: and Cc: headers are copied, but +not including any of the current user's email addresses (as configured +in primary_mail or other_email in the .notmuch\-config file) in the +recipient list + +It also builds a suitable new subject, including Re: at the front (if +not already present), and adding the message IDs of the messages being +replied to to the References list and setting the In\-Reply\-To: field +correctly. + +Finally, the original contents of the emails are quoted by prefixing +each line with '> ' and included in the body. + +The resulting message template is output to stdout. + +Supported options for +.B reply +include +.RS +.TP 4 +.BR \-\-format= ( default | headers\-only ) +.RS +.TP 4 +.BR default +Includes subject and quoted message body. +.TP +.BR headers\-only +Only produces In\-Reply\-To, References, To, Cc, and Bcc headers. +.RE +.RE + +See \fBnotmuch-search-terms\fR(7) +for details of the supported syntax for . + +Note: It is most common to use +.B "notmuch reply" +with a search string matching a single message, (such as +id:), but it can be useful to reply to several messages at +once. For example, when a series of patches are sent in a single +thread, replying to the entire thread allows for the reply to comment +on issue found in multiple patches. +.RE +.RE + +.SH SEE ALSO + +\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1), +\fBnotmuch-dump\fR(5), \fBnotmuch-hooks\fR(5), \fBnotmuch-new\fR(1), +\fBnotmuch-part\fR(1), \fBnotmuch-restore\fR(1), +\fBnotmuch-search\fR(1), \fBnotmuch-search-terms\fR(7), +\fBnotmuch-show\fR(1), \fBnotmuch-tag\fR(1) diff --git a/man/man1/notmuch-restore.1 b/man/man1/notmuch-restore.1 new file mode 100644 index 00000000..2191df01 --- /dev/null +++ b/man/man1/notmuch-restore.1 @@ -0,0 +1,39 @@ +.TH NOTMUCH-RESTORE 1 2011-12-04 "Notmuch 0.10.2" +.SH NAME +notmuch-restore \- Restores the tags from the given file (see notmuch dump). + +.SH SYNOPSIS + +.B "notmuch restore" +.RB [ "--accumulate" ] +.RI "[ <" filename "> ]" + +.SH DESCRIPTION + +Restores the tags from the given file (see +.BR "notmuch dump" ")." + +The input is read from the given filename, if any, or from stdin. + +Note: The dump file format is specifically chosen to be +compatible with the format of files produced by sup-dump. +So if you've previously been using sup for mail, then the +.B "notmuch restore" +command provides you a way to import all of your tags (or labels as +sup calls them). + +The --accumulate switch causes the union of the existing and new tags to be +applied, instead of replacing each message's tags as they are read in from the +dump file. + +See \fBnotmuch-search-terms\fR(7) +for details of the supported syntax for . + +.RE +.SH SEE ALSO + +\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1), +\fBnotmuch-hooks\fR(5), \fBnotmuch-new\fR(1), \fBnotmuch-part\fR(1), +\fBnotmuch-reply\fR(1), \fBnotmuch-dump\fR(1), +\fBnotmuch-search\fR(1), \fBnotmuch-search-terms\fR(7), +\fBnotmuch-show\fR(1), \fBnotmuch-tag\fR(1) diff --git a/man/man1/notmuch-search.1 b/man/man1/notmuch-search.1 new file mode 100644 index 00000000..0bc3f40d --- /dev/null +++ b/man/man1/notmuch-search.1 @@ -0,0 +1,121 @@ +.TH NOTMUCH-SEARCH 1 2011-12-04 "Notmuch 0.10.2" +.SH NAME +notmuch-search \- Search for messages matching the given search terms. +.SH SYNOPSIS + +.B notmuch search +.RI [ options "...] <" search-term ">..." + +.SH DESCRIPTION + +Search for messages matching the given search terms, and display as +results the threads containing the matched messages. + +The output consists of one line per thread, giving a thread ID, the +date of the newest (or oldest, depending on the sort option) matched +message in the thread, the number of matched messages and total +messages in the thread, the names of all participants in the thread, +and the subject of the newest (or oldest) message. + +See \fBnotmuch-search-terms\fR(7) +for details of the supported syntax for . + +Supported options for +.B search +include +.RS 4 +.TP 4 +.BR \-\-format= ( json | text ) + +Presents the results in either JSON or plain-text (default). +.RE + +.RS 4 +.TP 4 +.B \-\-output=(summary|threads|messages|files|tags) + +.RS 4 +.TP 4 +.B summary + +Output a summary of each thread with any message matching the search +terms. The summary includes the thread ID, date, the number of +messages in the thread (both the number matched and the total number), +the authors of the thread and the subject. +.RE +.RS 4 +.TP 4 +.B threads + +Output the thread IDs of all threads with any message matching the +search terms, either one per line (\-\-format=text) or as a JSON array +(\-\-format=json). +.RE +.RS 4 +.TP 4 +.B messages + +Output the message IDs of all messages matching the search terms, +either one per line (\-\-format=text) or as a JSON array +(\-\-format=json). +.RE +.RS 4 +.TP 4 +.B files + +Output the filenames of all messages matching the search terms, either +one per line (\-\-format=text) or as a JSON array (\-\-format=json). +.RE +.RS 4 +.TP 4 +.B tags + +Output all tags that appear on any message matching the search terms, +either one per line (\-\-format=text) or as a JSON array +(\-\-format=json). +.RE +.RE + +.RS 4 +.TP 4 +.BR \-\-sort= ( newest\-first | oldest\-first ) + +This option can be used to present results in either chronological order +.RB ( oldest\-first ) +or reverse chronological order +.RB ( newest\-first ). + +Note: The thread order will be distinct between these two options +(beyond being simply reversed). When sorting by +.B oldest\-first +the threads will be sorted by the oldest message in each thread, but +when sorting by +.B newest\-first +the threads will be sorted by the newest message in each thread. + +By default, results will be displayed in reverse chronological order, +(that is, the newest results will be displayed first). +.RE + +.RS 4 +.TP 4 +.BR \-\-offset=[\-]N + +Skip displaying the first N results. With the leading '\-', start at the Nth +result from the end. +.RE + +.RS 4 +.TP 4 +.BR \-\-limit=N + +Limit the number of displayed results to N. +.RE + +.SH SEE ALSO + +\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1), +\fBnotmuch-dump\fR(5), \fBnotmuch-hooks\fR(5), \fBnotmuch-part\fR(1), +\fBnotmuch-reply\fR(1), \fBnotmuch-reply\fR(1), +\fBnotmuch-restore\fR(1), \fBnotmuch-search-terms\fR(7), +\fBnotmuch-show\fR(1), \fBnotmuch-tag\fR(1) diff --git a/man/man1/notmuch-setup.1 b/man/man1/notmuch-setup.1 new file mode 120000 index 00000000..5c78dc87 --- /dev/null +++ b/man/man1/notmuch-setup.1 @@ -0,0 +1 @@ +notmuch.1 \ No newline at end of file diff --git a/man/man1/notmuch-show.1 b/man/man1/notmuch-show.1 new file mode 100644 index 00000000..b2301d82 --- /dev/null +++ b/man/man1/notmuch-show.1 @@ -0,0 +1,145 @@ +.TH NOTMUCH-SHOW 1 2011-12-04 "Notmuch 0.10.2" +.SH NAME +notmuch-show \- Show messages matching the given search terms. +.SH SYNOPSIS + +.B notmuch show +.RI "[" options "...] <" search-term ">..." + +.SH DESCRIPTION + +Shows all messages matching the search terms. + +See \fBnotmuch-search-terms\fR(7) +for details of the supported syntax for . + +The messages will be grouped and sorted based on the threading (all +replies to a particular message will appear immediately after that +message in date order). The output is not indented by default, but +depth tags are printed so that proper indentation can be performed by +a post-processor (such as the emacs interface to notmuch). + +Supported options for +.B show +include +.RS 4 +.TP 4 +.B \-\-entire\-thread + +By default only those messages that match the search terms will be +displayed. With this option, all messages in the same thread as any +matched message will be displayed. +.RE + +.RS 4 +.TP 4 +.B \-\-format=(text|json|mbox|raw) + +.RS 4 +.TP 4 +.BR text " (default for messages)" + +The default plain-text format has all text-content MIME parts +decoded. Various components in the output, +.RB ( message ", " header ", " body ", " attachment ", and MIME " part ), +will be delimited by easily-parsed markers. Each marker consists of a +Control-L character (ASCII decimal 12), the name of the marker, and +then either an opening or closing brace, ('{' or '}'), to either open +or close the component. For a multipart MIME message, these parts will +be nested. +.RE +.RS 4 +.TP 4 +.B json + +The output is formatted with Javascript Object Notation (JSON). This +format is more robust than the text format for automated +processing. The nested structure of multipart MIME messages is +reflected in nested JSON output. JSON output always includes all +messages in a matching thread; in effect +.B \-\-format=json +implies +.B \-\-entire\-thread + +.RE +.RS 4 +.TP 4 +.B mbox + +All matching messages are output in the traditional, Unix mbox format +with each message being prefixed by a line beginning with "From " and +a blank line separating each message. Lines in the message content +beginning with "From " (preceded by zero or more '>' characters) have +an additional '>' character added. This reversible escaping +is termed "mboxrd" format and described in detail here: + +.nf +.nh +http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/mail-mbox-formats.html +.hy +.fi +. +.RE +.RS 4 +.TP 4 +.BR raw " (default for a single part, see \-\-part)" + +For a message, the original, raw content of the email message is +output. Consumers of this format should expect to implement MIME +decoding and similar functions. + +For a single part (\-\-part) the raw part content is output after +performing any necessary MIME decoding. + +The raw format must only be used with search terms matching single +message. +.RE +.RE + +.RS 4 +.TP 4 +.B \-\-part=N + +Output the single decoded MIME part N of a single message. The search +terms must match only a single message. Message parts are numbered in +a depth-first walk of the message MIME structure, and are identified +in the 'json' or 'text' output formats. +.RE + +.RS 4 +.TP 4 +.B \-\-verify + +Compute and report the validity of any MIME cryptographic signatures +found in the selected content (ie. "multipart/signed" parts). Status +of the signature will be reported (currently only supported with +--format=json), and the multipart/signed part will be replaced by the +signed data. +.RE + +.RS 4 +.TP 4 +.B \-\-decrypt + +Decrypt any MIME encrypted parts found in the selected content +(ie. "multipart/encrypted" parts). Status of the decryption will be +reported (currently only supported with --format=json) and the +multipart/encrypted part will be replaced by the decrypted +content. +.RE + +A common use of +.B notmuch show +is to display a single thread of email messages. For this, use a +search term of "thread:" as can be seen in the first +column of output from the +.B notmuch search +command. + +.SH SEE ALSO + +\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1), +\fBnotmuch-dump\fR(5), \fBnotmuch-hooks\fR(5), \fBnotmuch-new\fR(1), +\fBnotmuch-part\fR(1), \fBnotmuch-reply\fR(1), +\fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1), +\fBnotmuch-search-terms\fR(7), \fBnotmuch-tag\fR(1) diff --git a/man/man1/notmuch-tag.1 b/man/man1/notmuch-tag.1 new file mode 100644 index 00000000..993911b9 --- /dev/null +++ b/man/man1/notmuch-tag.1 @@ -0,0 +1,32 @@ +.TH NOTMUCH-TAG 1 2011-12-04 "Notmuch 0.10.2" +.SH NAME +notmuch-tag \- Add/remove tags for all messages matching the search terms. + +.SH SYNOPSIS +.B notmuch tag +.RI "+<" tag> "|\-<" tag "> [...] [\-\-] <" search-term ">..." + +.SH DESCRIPTION + +Add/remove tags for all messages matching the search terms. + +See \fBnotmuch-search-terms\fR(7) +for details of the supported syntax for . + +Tags prefixed by '+' are added while those prefixed by '\-' are +removed. For each message, tag removal is performed before tag +addition. + +The beginning of is recognized by the first +argument that begins with neither '+' nor '\-'. Support for +an initial search term beginning with '+' or '\-' is provided +by allowing the user to specify a "\-\-" argument to separate +the tags from the search terms. + +.SH SEE ALSO + +\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1), +\fBnotmuch-dump\fR(5), \fBnotmuch-hooks\fR(5), \fBnotmuch-new\fR(1), +\fBnotmuch-part\fR(1), \fBnotmuch-reply\fR(1), +\fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1), +\fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1) diff --git a/man/man1/notmuch.1 b/man/man1/notmuch.1 new file mode 100644 index 00000000..424ca369 --- /dev/null +++ b/man/man1/notmuch.1 @@ -0,0 +1,148 @@ +.\" notmuch - Not much of an email program, (just index, search and tagging) +.\" +.\" Copyright © 2009 Carl Worth +.\" +.\" 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 this program. If not, see http://www.gnu.org/licenses/ . +.\" +.\" Author: Carl Worth +.TH NOTMUCH 1 2011-12-04 "Notmuch 0.10.2" +.SH NAME +notmuch \- thread-based email index, search, and tagging +.SH SYNOPSIS +.B notmuch +.IR command " [" args " ...]" +.SH DESCRIPTION +Notmuch is a command-line based program for indexing, searching, +reading, and tagging large collections of email messages. + +This page describes how to get started using notmuch from the command +line, and gives a brief overview of the commands available. For more +information on e.g. +.B notmuch show +consult the \fBnotmuch-show\fR(1) man page, also accessible via +.B notmuch help show + +The quickest way to get started with Notmuch is to simply invoke the +.B notmuch +command with no arguments, which will interactively guide you through +the process of indexing your mail. +.SH NOTE +While the command-line program +.B 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 more about alternate +interfaces to notmuch. The emacs-based interface to notmuch (available under +.B emacs/ +in the Notmuch source distribution) is probably the most widely used at +this time. + +.SH COMMANDS + + +.SS SETUP + +The +.B notmuch setup +command is used to configure Notmuch for first use, (or to reconfigure +it later). + +The setup command will prompt for your full name, your primary email +address, any alternate email addresses you use, and the directory +containing your email archives. Your answers will be written to a +configuration file in ${NOTMUCH_CONFIG} (if set) or +${HOME}/.notmuch-config . This configuration file will be created with +descriptive comments, making it easy to edit by hand later to change the +configuration. Or you can run +.B "notmuch setup" +again to change the configuration. + +The mail directory you specify can contain any number of +sub-directories and should primarily contain only files with individual +email messages (eg. maildir or mh archives are perfect). If there are +other, non-email files (such as indexes maintained by other email +programs) then notmuch will do its best to detect those and ignore +them. + +Mail storage that uses mbox format, (where one mbox file contains many +messages), will not work with notmuch. If that's how your mail is +currently stored, it is recommended you first convert it to maildir +format with a utility such as mb2md before running +.B "notmuch setup" . + +Invoking +.B notmuch +with no command argument will run +.B setup +if the setup command has not previously been completed. +.RE + +.SS OTHER COMMANDS + +Several of the notmuch commands accept search terms with a common +syntax. See \fNnotmuch-search-terms\fR(7) +for more details on the supported syntax. + +The +.BR search ", " show " and " count +commands are used to query the email database. + +The +.B reply +command is useful for preparing a template for an email reply. + +The +.B tag +command is the only command available for manipulating database +contents. + + +The +.BR dump " and " restore +commands can be used to create a textual dump of email tags for backup +purposes, and to restore from that dump. + +The +.B config +command can be used to get or set settings int the notmuch +configuration file. + +.SH ENVIRONMENT +The following environment variables can be used to control the +behavior of notmuch. +.TP +.B NOTMUCH_CONFIG +Specifies the location of the notmuch configuration file. Notmuch will +use ${HOME}/.notmuch\-config if this variable is not set. +.SH SEE ALSO + +\fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1), +\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5), \fBnotmuch-new\fR(1), +\fBnotmuch-part\fR(1), \fBnotmuch-reply\fR(1), +\fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1), +\fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1), +\fBnotmuch-tag\fR(1) + + +The notmuch website: +.B http://notmuchmail.org +.SH CONTACT +Feel free to send questions, comments, or kudos to the notmuch mailing +list . Subscription is not required before +posting, but is available from the notmuchmail.org website. + +Real-time interaction with the Notmuch community is available via IRC +(server: irc.freenode.net, channel: #notmuch). diff --git a/man/man5/notmuch-hooks.5 b/man/man5/notmuch-hooks.5 new file mode 100644 index 00000000..2c4e5527 --- /dev/null +++ b/man/man5/notmuch-hooks.5 @@ -0,0 +1,48 @@ +.TH NOTMUCH-HOOKS 5 2011-12-04 "Notmuch 0.10.2" + +.SH NAME +notmuch-hooks \- hooks for notmuch + +.SH SYNOPSIS + $DATABASEDIR/.notmuch/hooks/* + +.SH DESCRIPTION +Hooks are scripts (or arbitrary executables or symlinks to such) that notmuch +invokes before and after certain actions. These scripts reside in +the .notmuch/hooks directory within the database directory and must have +executable permissions. + +The currently available hooks are described below. +.RS 4 +.TP 4 +.B pre\-new +This hook is invoked by the +.B new +command before scanning or importing new messages into the database. If this +hook exits with a non-zero status, notmuch will abort further processing of the +.B new +command. + +Typically this hook is used for fetching or delivering new mail to be imported +into the database. +.RE +.RS 4 +.TP 4 +.B post\-new +This hook is invoked by the +.B new +command after new messages have been imported into the database and initial tags +have been applied. The hook will not be run if there have been any errors during +the scan or import. + +Typically this hook is used to perform additional query\-based tagging on the +imported messages. +.RE + +.SH SEE ALSO + +\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1), +\fBnotmuch-dump\fR(5), \fBnotmuch-new\fR(1), \fBnotmuch-part\fR(1), +\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1), +\fBnotmuch-search\fR(1), \fBnotmuch-search-terms\fR(7), +\fBnotmuch-show\fR(1), \fBnotmuch-tag\fR(1) diff --git a/man/man7/notmuch-search-terms.7 b/man/man7/notmuch-search-terms.7 new file mode 100644 index 00000000..a53565b8 --- /dev/null +++ b/man/man7/notmuch-search-terms.7 @@ -0,0 +1,141 @@ +.TH NOTMUCH-SEARCH-TERMS 7 2011-12-04 "Notmuch 0.10.2" + +.SH NAME +notmuch-search-terms \- Syntax for notmuch queries + +.SH SYNOPSIS + +.B notmuch count +.RI [ options... ] +.RI < search-term ">..." + +.B "notmuch dump" +.RI "[ <" filename "> ] [--]" +.RI "[ <" search-term ">...]" + +.B notmuch search +.RI [ options "...] <" search-term ">..." + +.B notmuch show +.RI "[" options "...] <" search-term ">..." + +.B notmuch tag +.RI "+<" tag> "|\-<" tag "> [...] [\-\-] <" search-term ">..." + + +.SH DESCRIPTION +Several notmuch commands accept a common syntax for search terms. + +The search terms can consist of free-form text (and quoted phrases) +which will match all messages that contain all of the given +terms/phrases in the body, the subject, or any of the sender or +recipient headers. + +As a special case, a search string consisting of exactly a single +asterisk ("*") will match all messages. + +In addition to free text, the following prefixes can be used to force +terms to match against specific portions of an email, (where + indicate user-supplied values): + + from: + + to: + + subject: + + attachment: + + tag: (or is:) + + id: + + thread: + + folder: + +The +.B from: +prefix is used to match the name or address of the sender of an email +message. + +The +.B to: +prefix is used to match the names or addresses of any recipient of an +email message, (whether To, Cc, or Bcc). + +Any term prefixed with +.B subject: +will match only text from the subject of an email. Searching for a +phrase in the subject is supported by including quotation marks around +the phrase, immediately following +.BR subject: . + +The +.B attachment: +prefix can be used to search for specific filenames (or extensions) of +attachments to email messages. + +For +.BR tag: " and " is: +valid tag values include +.BR inbox " and " unread +by default for new messages added by +.B notmuch new +as well as any other tag values added manually with +.BR "notmuch tag" . + +For +.BR id: , +message ID values are the literal contents of the Message\-ID: header +of email messages, but without the '<', '>' delimiters. + +The +.B thread: +prefix can be used with the thread ID values that are generated +internally by notmuch (and do not appear in email messages). These +thread ID values can be seen in the first column of output from +.B "notmuch search" + +The +.B folder: +prefix can be used to search for email message files that are +contained within particular directories within the mail store. Only +the directory components below the top-level mail database path are +available to be searched. + +In addition to individual terms, multiple terms can be +combined with Boolean operators ( +.BR and ", " or ", " not +, etc.). Each term in the query will be implicitly connected by a +logical AND if no explicit operator is provided, (except that terms +with a common prefix will be implicitly combined with OR until we get +Xapian defect #402 fixed). + +Parentheses can also be used to control the combination of the Boolean +operators, but will have to be protected from interpretation by the +shell, (such as by putting quotation marks around any parenthesized +expression). + +Finally, results can be restricted to only messages within a +particular time range, (based on the Date: header) with a syntax of: + + .. + +Each timestamp is a number representing the number of seconds since +1970\-01\-01 00:00:00 UTC. This is not the most convenient means of +expressing date ranges, but until notmuch is fixed to accept a more +convenient form, one can use the date program to construct +timestamps. For example, with the bash shell the following syntax would +specify a date range to return messages from 2009\-10\-01 until the +current time: + + $(date +%s \-d 2009\-10\-01)..$(date +%s) + +.SH SEE ALSO + +\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1), +\fBnotmuch-dump\fR(5), \fBnotmuch-hooks\fR(5), \fBnotmuch-part\fR(1), +\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1), +\fBnotmuch-search\fR(1), \fBnotmuch-search\fR(1), +\fBnotmuch-show\fR(1), \fBnotmuch-tag\fR(1) diff --git a/mime-node.c b/mime-node.c new file mode 100644 index 00000000..d26bb445 --- /dev/null +++ b/mime-node.c @@ -0,0 +1,265 @@ +/* notmuch - Not much of an email program, (just index and search) + * + * Copyright © 2009 Carl Worth + * Copyright © 2009 Keith Packard + * + * 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/ . + * + * Authors: Carl Worth + * Keith Packard + * Austin Clements + */ + +#include "notmuch-client.h" + +/* Context that gets inherited from the root node. */ +typedef struct mime_node_context { + /* Per-message resources. These are allocated internally and must + * be destroyed. */ + FILE *file; + GMimeStream *stream; + GMimeParser *parser; + GMimeMessage *mime_message; + + /* Context provided by the caller. */ + GMimeCipherContext *cryptoctx; + notmuch_bool_t decrypt; +} mime_node_context_t; + +static int +_mime_node_context_free (mime_node_context_t *res) +{ + if (res->mime_message) + g_object_unref (res->mime_message); + + if (res->parser) + g_object_unref (res->parser); + + if (res->stream) + g_object_unref (res->stream); + + if (res->file) + fclose (res->file); + + return 0; +} + +notmuch_status_t +mime_node_open (const void *ctx, notmuch_message_t *message, + GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, + mime_node_t **root_out) +{ + const char *filename = notmuch_message_get_filename (message); + mime_node_context_t *mctx; + mime_node_t *root; + notmuch_status_t status; + + root = talloc_zero (ctx, mime_node_t); + if (root == NULL) { + fprintf (stderr, "Out of memory.\n"); + status = NOTMUCH_STATUS_OUT_OF_MEMORY; + goto DONE; + } + + /* Create the tree-wide context */ + mctx = talloc_zero (root, mime_node_context_t); + if (mctx == NULL) { + fprintf (stderr, "Out of memory.\n"); + status = NOTMUCH_STATUS_OUT_OF_MEMORY; + goto DONE; + } + talloc_set_destructor (mctx, _mime_node_context_free); + + mctx->file = fopen (filename, "r"); + if (! mctx->file) { + fprintf (stderr, "Error opening %s: %s\n", filename, strerror (errno)); + status = NOTMUCH_STATUS_FILE_ERROR; + goto DONE; + } + + mctx->stream = g_mime_stream_file_new (mctx->file); + g_mime_stream_file_set_owner (GMIME_STREAM_FILE (mctx->stream), FALSE); + + mctx->parser = g_mime_parser_new_with_stream (mctx->stream); + + mctx->mime_message = g_mime_parser_construct_message (mctx->parser); + + mctx->cryptoctx = cryptoctx; + mctx->decrypt = decrypt; + + /* Create the root node */ + root->part = GMIME_OBJECT (mctx->mime_message); + root->envelope_file = message; + root->nchildren = 1; + root->ctx = mctx; + + *root_out = root; + return NOTMUCH_STATUS_SUCCESS; + +DONE: + talloc_free (root); + return status; +} + +static int +_signature_validity_free (GMimeSignatureValidity **proxy) +{ + g_mime_signature_validity_free (*proxy); + return 0; +} + +static mime_node_t * +_mime_node_create (const mime_node_t *parent, GMimeObject *part) +{ + mime_node_t *node = talloc_zero (parent, mime_node_t); + GError *err = NULL; + + /* Set basic node properties */ + node->part = part; + node->ctx = parent->ctx; + if (!talloc_reference (node, node->ctx)) { + fprintf (stderr, "Out of memory.\n"); + talloc_free (node); + return NULL; + } + + /* Deal with the different types of parts */ + if (GMIME_IS_PART (part)) { + node->nchildren = 0; + } else if (GMIME_IS_MULTIPART (part)) { + node->nchildren = g_mime_multipart_get_count (GMIME_MULTIPART (part)); + } else if (GMIME_IS_MESSAGE_PART (part)) { + /* Promote part to an envelope and open it */ + GMimeMessagePart *message_part = GMIME_MESSAGE_PART (part); + GMimeMessage *message = g_mime_message_part_get_message (message_part); + node->envelope_part = message_part; + node->part = GMIME_OBJECT (message); + node->nchildren = 1; + } else { + fprintf (stderr, "Warning: Unknown mime part type: %s.\n", + g_type_name (G_OBJECT_TYPE (part))); + talloc_free (node); + return NULL; + } + + /* Handle PGP/MIME parts */ + if (GMIME_IS_MULTIPART_ENCRYPTED (part) + && node->ctx->cryptoctx && node->ctx->decrypt) { + if (node->nchildren != 2) { + /* this violates RFC 3156 section 4, so we won't bother with it. */ + fprintf (stderr, "Error: %d part(s) for a multipart/encrypted " + "message (must be exactly 2)\n", + node->nchildren); + } else { + GMimeMultipartEncrypted *encrypteddata = + GMIME_MULTIPART_ENCRYPTED (part); + node->decrypt_attempted = TRUE; + node->decrypted_child = g_mime_multipart_encrypted_decrypt + (encrypteddata, node->ctx->cryptoctx, &err); + if (node->decrypted_child) { + node->decrypt_success = node->verify_attempted = TRUE; + node->sig_validity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata); + } else { + fprintf (stderr, "Failed to decrypt part: %s\n", + (err ? err->message : "no error explanation given")); + } + } + } else if (GMIME_IS_MULTIPART_SIGNED (part) && node->ctx->cryptoctx) { + if (node->nchildren != 2) { + /* this violates RFC 3156 section 5, so we won't bother with it. */ + fprintf (stderr, "Error: %d part(s) for a multipart/signed message " + "(must be exactly 2)\n", + node->nchildren); + } else { + /* For some reason the GMimeSignatureValidity returned + * here is not a const (inconsistent with that + * returned by + * g_mime_multipart_encrypted_get_signature_validity, + * and therefore needs to be properly disposed of. + * + * In GMime 2.6, they're both non-const, so we'll be able + * to clean up this asymmetry. */ + GMimeSignatureValidity *sig_validity = g_mime_multipart_signed_verify + (GMIME_MULTIPART_SIGNED (part), node->ctx->cryptoctx, &err); + node->verify_attempted = TRUE; + node->sig_validity = sig_validity; + if (sig_validity) { + GMimeSignatureValidity **proxy = + talloc (node, GMimeSignatureValidity *); + *proxy = sig_validity; + talloc_set_destructor (proxy, _signature_validity_free); + } + } + } + + if (node->verify_attempted && !node->sig_validity) + fprintf (stderr, "Failed to verify signed part: %s\n", + (err ? err->message : "no error explanation given")); + + if (err) + g_error_free (err); + + return node; +} + +mime_node_t * +mime_node_child (const mime_node_t *parent, int child) +{ + GMimeObject *sub; + + if (!parent || child < 0 || child >= parent->nchildren) + return NULL; + + if (GMIME_IS_MULTIPART (parent->part)) { + if (child == 1 && parent->decrypted_child) + sub = parent->decrypted_child; + else + sub = g_mime_multipart_get_part + (GMIME_MULTIPART (parent->part), child); + } else if (GMIME_IS_MESSAGE (parent->part)) { + sub = g_mime_message_get_mime_part (GMIME_MESSAGE (parent->part)); + } else { + /* This should have been caught by message_part_create */ + INTERNAL_ERROR ("Unexpected GMimeObject type: %s", + g_type_name (G_OBJECT_TYPE (parent->part))); + } + return _mime_node_create (parent, sub); +} + +static mime_node_t * +_mime_node_seek_dfs_walk (mime_node_t *node, int *n) +{ + mime_node_t *ret = NULL; + int i; + + if (*n == 0) + return node; + + *n -= 1; + for (i = 0; i < node->nchildren && !ret; i++) { + mime_node_t *child = mime_node_child (node, i); + ret = _mime_node_seek_dfs_walk (child, n); + if (!ret) + talloc_free (child); + } + return ret; +} + +mime_node_t * +mime_node_seek_dfs (mime_node_t *node, int n) +{ + if (n < 0) + return NULL; + return _mime_node_seek_dfs_walk (node, &n); +} diff --git a/notmuch-client.h b/notmuch-client.h index c602e2e0..517c010a 100644 --- a/notmuch-client.h +++ b/notmuch-client.h @@ -162,7 +162,7 @@ char * query_string_from_args (void *ctx, int argc, char *argv[]); notmuch_status_t -show_message_body (const char *filename, +show_message_body (notmuch_message_t *message, const notmuch_show_format_t *format, notmuch_show_params_t *params); @@ -241,5 +241,93 @@ notmuch_run_hook (const char *db_path, const char *hook); notmuch_bool_t debugger_is_active (void); +/* mime-node.c */ + +/* mime_node_t represents a single node in a MIME tree. A MIME tree + * abstracts the different ways of traversing different types of MIME + * parts, allowing a MIME message to be viewed as a generic tree of + * parts. Message-type parts have one child, multipart-type parts + * have multiple children, and leaf parts have zero children. + */ +typedef struct mime_node { + /* The MIME object of this part. This will be a GMimeMessage, + * GMimePart, GMimeMultipart, or a subclass of one of these. + * + * This will never be a GMimeMessagePart because GMimeMessagePart + * is structurally redundant with GMimeMessage. If this part is a + * message (that is, 'part' is a GMimeMessage), then either + * envelope_file will be set to a notmuch_message_t (for top-level + * messages) or envelope_part will be set to a GMimeMessagePart + * (for embedded message parts). + */ + GMimeObject *part; + + /* If part is a GMimeMessage, these record the envelope of the + * message: either a notmuch_message_t representing a top-level + * message, or a GMimeMessagePart representing a MIME part + * containing a message. + */ + notmuch_message_t *envelope_file; + GMimeMessagePart *envelope_part; + + /* The number of children of this part. */ + int nchildren; + + /* True if decryption of this part was attempted. */ + notmuch_bool_t decrypt_attempted; + /* True if decryption of this part's child succeeded. In this + * case, the decrypted part is substituted for the second child of + * this part (which would usually be the encrypted data). */ + notmuch_bool_t decrypt_success; + + /* True if signature verification on this part was attempted. */ + notmuch_bool_t verify_attempted; + /* For signed or encrypted containers, the validity of the + * signature. May be NULL if signature verification failed. If + * there are simply no signatures, this will be non-NULL with an + * empty signers list. */ + const GMimeSignatureValidity *sig_validity; + + /* Internal: Context inherited from the root iterator. */ + struct mime_node_context *ctx; + + /* Internal: For successfully decrypted multipart parts, the + * decrypted part to substitute for the second child. */ + GMimeObject *decrypted_child; +} mime_node_t; + +/* Construct a new MIME node pointing to the root message part of + * message. If cryptoctx is non-NULL, it will be used to verify + * signatures on any child parts. If decrypt is true, then cryptoctx + * will additionally be used to decrypt any encrypted child parts. + * + * Return value: + * + * NOTMUCH_STATUS_SUCCESS: Root node is returned in *node_out. + * + * NOTMUCH_STATUS_FILE_ERROR: Failed to open message file. + * + * NOTMUCH_STATUS_OUT_OF_MEMORY: Out of memory. + */ +notmuch_status_t +mime_node_open (const void *ctx, notmuch_message_t *message, + GMimeCipherContext *cryptoctx, notmuch_bool_t decrypt, + mime_node_t **node_out); + +/* Return a new MIME node for the requested child part of parent. + * parent will be used as the talloc context for the returned child + * node. + * + * In case of any failure, this function returns NULL, (after printing + * an error message on stderr). + */ +mime_node_t * +mime_node_child (const mime_node_t *parent, int child); + +/* Return the nth child of node in a depth-first traversal. If n is + * 0, returns node itself. Returns NULL if there is no such part. */ +mime_node_t * +mime_node_seek_dfs (mime_node_t *node, int n); + #include "command-line-arguments.h" #endif diff --git a/notmuch-reply.c b/notmuch-reply.c index 7242310a..000f6dad 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -546,8 +546,7 @@ notmuch_reply_format_default(void *ctx, notmuch_message_get_header (message, "date"), notmuch_message_get_header (message, "from")); - show_message_body (notmuch_message_get_filename (message), - format, params); + show_message_body (message, format, params); notmuch_message_destroy (message); } @@ -613,62 +612,61 @@ notmuch_reply_format_headers_only(void *ctx, return 0; } +enum { + FORMAT_DEFAULT, + FORMAT_HEADERS_ONLY, +}; + int notmuch_reply_command (void *ctx, int argc, char *argv[]) { notmuch_config_t *config; notmuch_database_t *notmuch; notmuch_query_t *query; - char *opt, *query_string; - int i, ret = 0; + char *query_string; + int opt_index, ret = 0; int (*reply_format_func)(void *ctx, notmuch_config_t *config, notmuch_query_t *query, notmuch_show_params_t *params); notmuch_show_params_t params = { .part = -1 }; + int format = FORMAT_DEFAULT; + notmuch_bool_t decrypt = FALSE; + + notmuch_opt_desc_t options[] = { + { NOTMUCH_OPT_KEYWORD, &format, "format", 'f', + (notmuch_keyword_t []){ { "default", FORMAT_DEFAULT }, + { "headers-only", FORMAT_HEADERS_ONLY }, + { 0, 0 } } }, + { NOTMUCH_OPT_BOOLEAN, &decrypt, "decrypt", 'd', 0 }, + { 0, 0, 0, 0, 0 } + }; - reply_format_func = notmuch_reply_format_default; - - argc--; argv++; /* skip subcommand argument */ + opt_index = parse_arguments (argc, argv, options, 1); + if (opt_index < 0) { + /* diagnostics already printed */ + return 1; + } - for (i = 0; i < argc && argv[i][0] == '-'; i++) { - if (strcmp (argv[i], "--") == 0) { - i++; - break; - } - if (STRNCMP_LITERAL (argv[i], "--format=") == 0) { - opt = argv[i] + sizeof ("--format=") - 1; - if (strcmp (opt, "default") == 0) { - reply_format_func = notmuch_reply_format_default; - } else if (strcmp (opt, "headers-only") == 0) { - reply_format_func = notmuch_reply_format_headers_only; - } else { - fprintf (stderr, "Invalid value for --format: %s\n", opt); - return 1; - } - } else if ((STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) { - if (params.cryptoctx == NULL) { - GMimeSession* session = g_object_new(g_mime_session_get_type(), NULL); - if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg"))) { - fprintf (stderr, "Failed to construct gpg context.\n"); - } else { - params.decrypt = TRUE; - g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE); - } - g_object_unref (session); - session = NULL; - } + if (format == FORMAT_HEADERS_ONLY) + reply_format_func = notmuch_reply_format_headers_only; + else + reply_format_func = notmuch_reply_format_default; + + if (decrypt) { + GMimeSession* session = g_object_new (g_mime_session_get_type(), NULL); + params.cryptoctx = g_mime_gpg_context_new (session, "gpg"); + if (params.cryptoctx) { + g_mime_gpg_context_set_always_trust ((GMimeGpgContext*) params.cryptoctx, FALSE); + params.decrypt = TRUE; } else { - fprintf (stderr, "Unrecognized option: %s\n", argv[i]); - return 1; + fprintf (stderr, "Failed to construct gpg context.\n"); } + g_object_unref (session); } - argc -= i; - argv += i; - config = notmuch_config_open (ctx, NULL, NULL); if (config == NULL) return 1; - query_string = query_string_from_args (ctx, argc, argv); + query_string = query_string_from_args (ctx, argc-opt_index, argv+opt_index); if (query_string == NULL) { fprintf (stderr, "Out of memory\n"); return 1; diff --git a/notmuch-show.c b/notmuch-show.c index 19fb49f2..0200b9c4 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -758,8 +758,7 @@ show_message (void *ctx, } if (format->part_content) - show_message_body (notmuch_message_get_filename (message), - format, params); + show_message_body (message, format, params); if (params->part <= 0) { fputs (format->body_end, stdout); diff --git a/notmuch.1 b/notmuch.1 deleted file mode 100644 index a5828bc5..00000000 --- a/notmuch.1 +++ /dev/null @@ -1,776 +0,0 @@ -.\" notmuch - Not much of an email program, (just index, search and tagging) -.\" -.\" Copyright © 2009 Carl Worth -.\" -.\" 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 this program. If not, see http://www.gnu.org/licenses/ . -.\" -.\" Author: Carl Worth -.TH NOTMUCH 1 2012-01-13 "Notmuch 0.11" -.SH NAME -notmuch \- thread-based email index, search, and tagging -.SH SYNOPSIS -.B notmuch -.IR command " [" args " ...]" -.SH DESCRIPTION -Notmuch is a command-line based program for indexing, searching, -reading, and tagging large collections of email messages. - -The quickest way to get started with Notmuch is to simply invoke the -.B notmuch -command with no arguments, which will interactively guide you through -the process of indexing your mail. -.SH NOTE -While the command-line program -.B 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 more about alternate -interfaces to notmuch. -.SH COMMANDS -The -.BR setup -command is used to configure Notmuch for first use, (or to reconfigure -it later). -.RS 4 -.TP 4 -.B setup - -Interactively sets up notmuch for first use. - -The setup command will prompt for your full name, your primary email -address, any alternate email addresses you use, and the directory -containing your email archives. Your answers will be written to a -configuration file in ${NOTMUCH_CONFIG} (if set) or -${HOME}/.notmuch-config . This configuration file will be created with -descriptive comments, making it easy to edit by hand later to change the -configuration. Or you can run -.B "notmuch setup" -again to change the configuration. - -The mail directory you specify can contain any number of -sub-directories and should primarily contain only files with individual -email messages (eg. maildir or mh archives are perfect). If there are -other, non-email files (such as indexes maintained by other email -programs) then notmuch will do its best to detect those and ignore -them. - -Mail storage that uses mbox format, (where one mbox file contains many -messages), will not work with notmuch. If that's how your mail is -currently stored, it is recommended you first convert it to maildir -format with a utility such as mb2md before running -.B "notmuch setup" . - -Invoking -.B notmuch -with no command argument will run -.B setup -if the setup command has not previously been completed. -.RE - -The -.B new -command is used to incorporate new mail into the notmuch database. -.RS 4 -.TP 4 -.BR new " [options...]" - -Find and import any new messages to the database. - -The -.B new -command scans all sub-directories of the database, performing -full-text indexing on new messages that are found. Each new message -will automatically be tagged with both the -.BR inbox " and " unread -tags. - -You should run -.B "notmuch new" -once after first running -.B "notmuch setup" -to create the initial database. The first run may take a long time if -you have a significant amount of mail (several hundred thousand -messages or more). Subsequently, you should run -.B "notmuch new" -whenever new mail is delivered and you wish to incorporate it into the -database. These subsequent runs will be much quicker than the initial -run. - -Invoking -.B notmuch -with no command argument will run -.B new -if -.B "notmuch setup" -has previously been completed, but -.B "notmuch new" -has not previously been run. - -The -.B new -command supports hooks. See the -.B "HOOKS" -section below for more details on hooks. - -Supported options for -.B new -include -.RS 4 -.TP 4 -.BR \-\-no\-hooks - -Prevents hooks from being run. -.RE -.RE - -Several of the notmuch commands accept search terms with a common -syntax. See the -.B "SEARCH SYNTAX" -section below for more details on the supported syntax. - -The -.BR search ", " show " and " count -commands are used to query the email database. -.RS 4 -.TP 4 -.BR search " [options...] ..." - -Search for messages matching the given search terms, and display as -results the threads containing the matched messages. - -The output consists of one line per thread, giving a thread ID, the -date of the newest (or oldest, depending on the sort option) matched -message in the thread, the number of matched messages and total -messages in the thread, the names of all participants in the thread, -and the subject of the newest (or oldest) message. - -Supported options for -.B search -include -.RS 4 -.TP 4 -.BR \-\-format= ( json | text ) - -Presents the results in either JSON or plain-text (default). -.RE - -.RS 4 -.TP 4 -.B \-\-output=(summary|threads|messages|files|tags) - -.RS 4 -.TP 4 -.B summary - -Output a summary of each thread with any message matching the search -terms. The summary includes the thread ID, date, the number of -messages in the thread (both the number matched and the total number), -the authors of the thread and the subject. -.RE -.RS 4 -.TP 4 -.B threads - -Output the thread IDs of all threads with any message matching the -search terms, either one per line (\-\-format=text) or as a JSON array -(\-\-format=json). -.RE -.RS 4 -.TP 4 -.B messages - -Output the message IDs of all messages matching the search terms, -either one per line (\-\-format=text) or as a JSON array -(\-\-format=json). -.RE -.RS 4 -.TP 4 -.B files - -Output the filenames of all messages matching the search terms, either -one per line (\-\-format=text) or as a JSON array (\-\-format=json). -.RE -.RS 4 -.TP 4 -.B tags - -Output all tags that appear on any message matching the search terms, -either one per line (\-\-format=text) or as a JSON array -(\-\-format=json). -.RE -.RE - -.RS 4 -.TP 4 -.BR \-\-sort= ( newest\-first | oldest\-first ) - -This option can be used to present results in either chronological order -.RB ( oldest\-first ) -or reverse chronological order -.RB ( newest\-first ). - -Note: The thread order will be distinct between these two options -(beyond being simply reversed). When sorting by -.B oldest\-first -the threads will be sorted by the oldest message in each thread, but -when sorting by -.B newest\-first -the threads will be sorted by the newest message in each thread. - -By default, results will be displayed in reverse chronological order, -(that is, the newest results will be displayed first). -.RE - -.RS 4 -.TP 4 -.BR \-\-offset=[\-]N - -Skip displaying the first N results. With the leading '\-', start at the Nth -result from the end. -.RE - -.RS 4 -.TP 4 -.BR \-\-limit=N - -Limit the number of displayed results to N. -.RE - -.RS 4 -See the -.B "SEARCH SYNTAX" -section below for details of the supported syntax for . -.RE -.TP -.BR show " [options...] ..." - -Shows all messages matching the search terms. - -The messages will be grouped and sorted based on the threading (all -replies to a particular message will appear immediately after that -message in date order). The output is not indented by default, but -depth tags are printed so that proper indentation can be performed by -a post-processor (such as the emacs interface to notmuch). - -Supported options for -.B show -include -.RS 4 -.TP 4 -.B \-\-entire\-thread - -By default only those messages that match the search terms will be -displayed. With this option, all messages in the same thread as any -matched message will be displayed. -.RE - -.RS 4 -.TP 4 -.B \-\-format=(text|json|mbox|raw) - -.RS 4 -.TP 4 -.BR text " (default for messages)" - -The default plain-text format has all text-content MIME parts -decoded. Various components in the output, -.RB ( message ", " header ", " body ", " attachment ", and MIME " part ), -will be delimited by easily-parsed markers. Each marker consists of a -Control-L character (ASCII decimal 12), the name of the marker, and -then either an opening or closing brace, ('{' or '}'), to either open -or close the component. For a multipart MIME message, these parts will -be nested. -.RE -.RS 4 -.TP 4 -.B json - -The output is formatted with Javascript Object Notation (JSON). This -format is more robust than the text format for automated -processing. The nested structure of multipart MIME messages is -reflected in nested JSON output. JSON output always includes all -messages in a matching thread; in effect -.B \-\-format=json -implies -.B \-\-entire\-thread - -.RE -.RS 4 -.TP 4 -.B mbox - -All matching messages are output in the traditional, Unix mbox format -with each message being prefixed by a line beginning with "From " and -a blank line separating each message. Lines in the message content -beginning with "From " (preceded by zero or more '>' characters) have -an additional '>' character added. This reversible escaping -is termed "mboxrd" format and described in detail here: - -.nf -.nh -http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/mail-mbox-formats.html -.hy -.fi -. -.RE -.RS 4 -.TP 4 -.BR raw " (default for a single part, see \-\-part)" - -For a message, the original, raw content of the email message is -output. Consumers of this format should expect to implement MIME -decoding and similar functions. - -For a single part (\-\-part) the raw part content is output after -performing any necessary MIME decoding. - -The raw format must only be used with search terms matching single -message. -.RE -.RE - -.RS 4 -.TP 4 -.B \-\-part=N - -Output the single decoded MIME part N of a single message. The search -terms must match only a single message. Message parts are numbered in -a depth-first walk of the message MIME structure, and are identified -in the 'json' or 'text' output formats. -.RE - -.RS 4 -.TP 4 -.B \-\-verify - -Compute and report the validity of any MIME cryptographic signatures -found in the selected content (ie. "multipart/signed" parts). Status -of the signature will be reported (currently only supported with ---format=json), and the multipart/signed part will be replaced by the -signed data. -.RE - -.RS 4 -.TP 4 -.B \-\-decrypt - -Decrypt any MIME encrypted parts found in the selected content -(ie. "multipart/encrypted" parts). Status of the decryption will be -reported (currently only supported with --format=json) and the -multipart/encrypted part will be replaced by the decrypted -content. -.RE - -A common use of -.B notmuch show -is to display a single thread of email messages. For this, use a -search term of "thread:" as can be seen in the first -column of output from the -.B notmuch search -command. - -See the -.B "SEARCH SYNTAX" -section below for details of the supported syntax for . -.RE -.RS 4 -.TP 4 -.BR count " [options...] ..." - -Count messages matching the search terms. - -The number of matching messages (or threads) is output to stdout. - -With no search terms, a count of all messages (or threads) in the database will -be displayed. - -Supported options for -.B count -include -.RS 4 -.TP 4 -.B \-\-output=(messages|threads) - -.RS 4 -.TP 4 -.B messages - -Output the number of matching messages. This is the default. -.RE -.RS 4 -.TP 4 -.B threads - -Output the number of matching threads. -.RE -.RE -.RE -.RE - -The -.B reply -command is useful for preparing a template for an email reply. -.RS 4 -.TP 4 -.BR reply " [options...] ..." - -Constructs a reply template for a set of messages. - -To make replying to email easier, -.B notmuch reply -takes an existing set of messages and constructs a suitable mail -template. The Reply-to header (if any, otherwise From:) is used for -the To: address. Vales from the To: and Cc: headers are copied, but -not including any of the current user's email addresses (as configured -in primary_mail or other_email in the .notmuch\-config file) in the -recipient list - -It also builds a suitable new subject, including Re: at the front (if -not already present), and adding the message IDs of the messages being -replied to to the References list and setting the In\-Reply\-To: field -correctly. - -Finally, the original contents of the emails are quoted by prefixing -each line with '> ' and included in the body. - -The resulting message template is output to stdout. - -Supported options for -.B reply -include -.RS -.TP 4 -.BR \-\-format= ( default | headers\-only ) -.RS -.TP 4 -.BR default -Includes subject and quoted message body. -.TP -.BR headers\-only -Only produces In\-Reply\-To, References, To, Cc, and Bcc headers. -.RE - -See the -.B "SEARCH SYNTAX" -section below for details of the supported syntax for . - -Note: It is most common to use -.B "notmuch reply" -with a search string matching a single message, (such as -id:), but it can be useful to reply to several messages at -once. For example, when a series of patches are sent in a single -thread, replying to the entire thread allows for the reply to comment -on issue found in multiple patches. -.RE -.RE - -The -.B tag -command is the only command available for manipulating database -contents. - -.RS 4 -.TP 4 -.BR tag " +|\- [...] [\-\-] ..." - -Add/remove tags for all messages matching the search terms. - -Tags prefixed by '+' are added while those prefixed by '\-' are -removed. For each message, tag removal is performed before tag -addition. - -The beginning of is recognized by the first -argument that begins with neither '+' nor '\-'. Support for -an initial search term beginning with '+' or '\-' is provided -by allowing the user to specify a "\-\-" argument to separate -the tags from the search terms. - -See the -.B "SEARCH SYNTAX" -section below for details of the supported syntax for . -.RE - -The -.BR dump " and " restore -commands can be used to create a textual dump of email tags for backup -purposes, and to restore from that dump. - -.RS 4 -.TP 4 -.BR dump " [] [--] []" - -Creates a plain-text dump of the tags of each message. - -Output is to the given filename, if any, or to stdout. Note that -using the filename argument is deprecated. - -These tags are the only data in the notmuch database that can't be -recreated from the messages themselves. The output of notmuch dump is -therefore the only critical thing to backup (and much more friendly to -incremental backup than the native database files.) - -With no search terms, a dump of all messages in the database will be -generated. A "--" argument instructs notmuch that the -remaining arguments are search terms. - -See the -.B "SEARCH SYNTAX" -section below for details of the supported syntax for . -.RE - -.TP -.BR restore " [--accumulate] []" - -Restores the tags from the given file (see -.BR "notmuch dump" ")." - -The input is read from the given filename, if any, or from stdin. - -Note: The dump file format is specifically chosen to be -compatible with the format of files produced by sup-dump. -So if you've previously been using sup for mail, then the -.B "notmuch restore" -command provides you a way to import all of your tags (or labels as -sup calls them). - -The --accumulate switch causes the union of the existing and new tags to be -applied, instead of replacing each message's tags as they are read in from the -dump file. -.RE - -The -.B part -command can used to output a single part of a multipart MIME message. - -.RS 4 -.TP 4 -.BR part " \-\-part= ..." - -Output a single MIME part of a message. - -A single decoded MIME part, with no encoding or framing, is output to -stdout. The search terms must match only a single message, otherwise -this command will fail. - -The part number should match the part "id" field output by the -"\-\-format=json" option of "notmuch show". If the message specified by -the search terms does not include a part with the specified "id" there -will be no output. - -See the -.B "SEARCH SYNTAX" -section below for details of the supported syntax for . -.RE - -The -.B config -command can be used to get or set settings int the notmuch -configuration file. - -.RS 4 -.TP 4 -.BR "config get "
. - -The value of the specified configuration item is printed to stdout. If -the item has multiple values, each value is separated by a newline -character. - -Available configuration items include at least - - database.path - - user.name - - user.primary_email - - user.other_email - - new.tags -.RE - -.RS 4 -.TP 4 -.BR "config set "
. " [values ...]" - -The specified configuration item is set to the given value. To -specify a multiple-value item, provide each value as a separate -command-line argument. - -If no values are provided, the specified configuration item will be -removed from the configuration file. -.RE - -.SH SEARCH SYNTAX -Several notmuch commands accept a common syntax for search terms. - -The search terms can consist of free-form text (and quoted phrases) -which will match all messages that contain all of the given -terms/phrases in the body, the subject, or any of the sender or -recipient headers. - -As a special case, a search string consisting of exactly a single -asterisk ("*") will match all messages. - -In addition to free text, the following prefixes can be used to force -terms to match against specific portions of an email, (where - indicate user-supplied values): - - from: - - to: - - subject: - - attachment: - - tag: (or is:) - - id: - - thread: - - folder: - -The -.B from: -prefix is used to match the name or address of the sender of an email -message. - -The -.B to: -prefix is used to match the names or addresses of any recipient of an -email message, (whether To, Cc, or Bcc). - -Any term prefixed with -.B subject: -will match only text from the subject of an email. Searching for a -phrase in the subject is supported by including quotation marks around -the phrase, immediately following -.BR subject: . - -The -.B attachment: -prefix can be used to search for specific filenames (or extensions) of -attachments to email messages. - -For -.BR tag: " and " is: -valid tag values include -.BR inbox " and " unread -by default for new messages added by -.B notmuch new -as well as any other tag values added manually with -.BR "notmuch tag" . - -For -.BR id: , -message ID values are the literal contents of the Message\-ID: header -of email messages, but without the '<', '>' delimiters. - -The -.B thread: -prefix can be used with the thread ID values that are generated -internally by notmuch (and do not appear in email messages). These -thread ID values can be seen in the first column of output from -.B "notmuch search" - -The -.B folder: -prefix can be used to search for email message files that are -contained within particular directories within the mail store. Only -the directory components below the top-level mail database path are -available to be searched. - -In addition to individual terms, multiple terms can be -combined with Boolean operators ( -.BR and ", " or ", " not -, etc.). Each term in the query will be implicitly connected by a -logical AND if no explicit operator is provided, (except that terms -with a common prefix will be implicitly combined with OR until we get -Xapian defect #402 fixed). - -Parentheses can also be used to control the combination of the Boolean -operators, but will have to be protected from interpretation by the -shell, (such as by putting quotation marks around any parenthesized -expression). - -Finally, results can be restricted to only messages within a -particular time range, (based on the Date: header) with a syntax of: - - .. - -Each timestamp is a number representing the number of seconds since -1970\-01\-01 00:00:00 UTC. This is not the most convenient means of -expressing date ranges, but until notmuch is fixed to accept a more -convenient form, one can use the date program to construct -timestamps. For example, with the bash shell the following syntax would -specify a date range to return messages from 2009\-10\-01 until the -current time: - - $(date +%s \-d 2009\-10\-01)..$(date +%s) -.SH HOOKS -Hooks are scripts (or arbitrary executables or symlinks to such) that notmuch -invokes before and after certain actions. These scripts reside in -the .notmuch/hooks directory within the database directory and must have -executable permissions. - -The currently available hooks are described below. -.RS 4 -.TP 4 -.B pre\-new -This hook is invoked by the -.B new -command before scanning or importing new messages into the database. If this -hook exits with a non-zero status, notmuch will abort further processing of the -.B new -command. - -Typically this hook is used for fetching or delivering new mail to be imported -into the database. -.RE -.RS 4 -.TP 4 -.B post\-new -This hook is invoked by the -.B new -command after new messages have been imported into the database and initial tags -have been applied. The hook will not be run if there have been any errors during -the scan or import. - -Typically this hook is used to perform additional query\-based tagging on the -imported messages. -.RE -.SH ENVIRONMENT -The following environment variables can be used to control the -behavior of notmuch. -.TP -.B NOTMUCH_CONFIG -Specifies the location of the notmuch configuration file. Notmuch will -use ${HOME}/.notmuch\-config if this variable is not set. -.SH SEE ALSO -The emacs-based interface to notmuch (available as -.B notmuch.el -in the Notmuch distribution). - -The notmuch website: -.B http://notmuchmail.org -.SH CONTACT -Feel free to send questions, comments, or kudos to the notmuch mailing -list . Subscription is not required before -posting, but is available from the notmuchmail.org website. - -Real-time interaction with the Notmuch community is available via IRC -(server: irc.freenode.net, channel: #notmuch). diff --git a/notmuch.c b/notmuch.c index c0ce026a..477a09cf 100644 --- a/notmuch.c +++ b/notmuch.c @@ -29,7 +29,6 @@ typedef struct command { command_function_t function; const char *arguments; const char *summary; - const char *documentation; } command_t; #define MAX_ALIAS_SUBSTITUTIONS 3 @@ -47,447 +46,40 @@ alias_t aliases[] = { static int notmuch_help_command (void *ctx, int argc, char *argv[]); -static const char search_terms_help[] = - "\tSeveral notmuch commands accept a common syntax for search\n" - "\tterms.\n" - "\n" - "\tThe search terms can consist of free-form text (and quoted\n" - "\tphrases) which will match all messages that contain all of\n" - "\tthe given terms/phrases in the body, the subject, or any of\n" - "\tthe sender or recipient headers.\n" - "\n" - "\tAs a special case, a search string consisting of exactly a\n" - "\tsingle asterisk (\"*\") will match all messages.\n" - "\n" - "\tIn addition to free text, the following prefixes can be used\n" - "\tto force terms to match against specific portions of an email,\n" - "\t(where indicate user-supplied values):\n" - "\n" - "\t\tfrom:\n" - "\t\tto:\n" - "\t\tsubject:\n" - "\t\tattachment:\n" - "\t\ttag: (or is:)\n" - "\t\tid:\n" - "\t\tthread:\n" - "\t\tfolder:\n" - "\n" - "\tThe from: prefix is used to match the name or address of\n" - "\tthe sender of an email message.\n" - "\n" - "\tThe to: prefix is used to match the names or addresses of\n" - "\tany recipient of an email message, (whether To, Cc, or Bcc).\n" - "\n" - "\tAny term prefixed with subject: will match only text from\n" - "\tthe subject of an email. Quoted phrases are supported when\n" - "\tsearching with: subject:\"this is a phrase\".\n" - "\n" - "\tFor tag: and is:, valid tag values include \"inbox\" and \"unread\"\n" - "\tby default for new messages added by \"notmuch new\" as well\n" - "\tas any other tag values added manually with \"notmuch tag\".\n" - "\n" - "\tFor id:, message ID values are the literal contents of the\n" - "\tMessage-ID: header of email messages, but without the '<','>'\n" - "\tdelimiters.\n" - "\n" - "\tThe thread: prefix can be used with the thread ID values that\n" - "\tare generated internally by notmuch (and do not appear in email\n" - "\tmessages). These thread ID values can be seen in the first\n" - "\tcolumn of output from \"notmuch search\".\n" - "\n" - "\tThe folder: prefix can be used to search for email message\n" - "\tfiles that are contained within particular directories within\n" - "\tthe mail store. Only the directory components below the top-level\n" - "\tmail database path are available to be searched.\n" - "\n" - "\tIn addition to individual terms, multiple terms can be\n" - "\tcombined with Boolean operators (\"and\", \"or\", \"not\", etc.).\n" - "\tEach term in the query will be implicitly connected by a\n" - "\tlogical AND if no explicit operator is provided, (except\n" - "\tthat terms with a common prefix will be implicitly combined\n" - "\twith OR until we get Xapian defect #402 fixed).\n" - "\n" - "\tParentheses can also be used to control the combination of\n" - "\tthe Boolean operators, but will have to be protected from\n" - "\tinterpretation by the shell, (such as by putting quotation\n" - "\tmarks around any parenthesized expression).\n" - "\n" - "\tFinally, results can be restricted to only messages within a\n" - "\tparticular time range, (based on the Date: header) with:\n" - "\n" - "\t\t..\n" - "\n" - "\tEach timestamp is a number representing the number of seconds\n" - "\tsince 1970-01-01 00:00:00 UTC. This is not the most convenient\n" - "\tmeans of expressing date ranges, but until notmuch is fixed to\n" - "\taccept a more convenient form, one can use the date program to\n" - "\tconstruct timestamps. For example, with the bash shell the\n" - "\tfollowing syntax would specify a date range to return messages\n" - "\tfrom 2009-10-01 until the current time:\n" - "\n" - "\t\t$(date +%%s -d 2009-10-01)..$(date +%%s)\n\n"; - -static const char hooks_help[] = - "\tHooks are scripts (or arbitrary executables or symlinks to such) that\n" - "\tnotmuch invokes before and after certain actions. These scripts reside\n" - "\tin the .notmuch/hooks directory within the database directory and must\n" - "\thave executable permissions.\n" - "\n" - "\tThe currently available hooks are described below.\n" - "\n" - "\tpre-new\n" - "\t\tThis hook is invoked by the new command before scanning or\n" - "\t\timporting new messages into the database. If this hook exits\n" - "\t\twith a non-zero status, notmuch will abort further processing\n" - "\t\tof the new command.\n" - "\n" - "\t\tTypically this hook is used for fetching or delivering new\n" - "\t\tmail to be imported into the database.\n" - "\n" - "\tpost-new\n" - "\t\tThis hook is invoked by the new command after new messages\n" - "\t\thave been imported into the database and initial tags have\n" - "\t\tbeen applied. The hook will not be run if there have been any\n" - "\t\terrors during the scan or import.\n" - "\n" - "\t\tTypically this hook is used to perform additional query-based\n" - "\t\ttagging on the imported messages.\n\n"; - static command_t commands[] = { { "setup", notmuch_setup_command, NULL, - "Interactively setup notmuch for first use.", - "\tThe setup command will prompt for your full name, your primary\n" - "\temail address, any alternate email addresses you use, and the\n" - "\tdirectory containing your email archives. Your answers will be\n" - "\twritten to a configuration file in ${NOTMUCH_CONFIG} (if set)\n" - "\tor ${HOME}/.notmuch-config.\n" - "\n" - "\tThis configuration file will be created with descriptive\n" - "\tcomments, making it easy to edit by hand later to change the\n" - "\tconfiguration. Or you can run \"notmuch setup\" again.\n" - "\n" - "\tInvoking notmuch with no command argument will run setup if\n" - "\tthe setup command has not previously been completed." }, + "Interactively setup notmuch for first use." }, { "new", notmuch_new_command, "[options...]", - "Find and import new messages to the notmuch database.", - "\tScans all sub-directories of the mail directory, performing\n" - "\tfull-text indexing on new messages that are found. Each new\n" - "\tmessage will be tagged as both \"inbox\" and \"unread\".\n" - "\n" - "\tYou should run \"notmuch new\" once after first running\n" - "\t\"notmuch setup\" to create the initial database. The first\n" - "\trun may take a long time if you have a significant amount of\n" - "\tmail (several hundred thousand messages or more).\n" - "\n" - "\tSubsequently, you should run \"notmuch new\" whenever new mail\n" - "\tis delivered and you wish to incorporate it into the database.\n" - "\tThese subsequent runs will be much quicker than the initial run.\n" - "\n" - "\tThe new command supports hooks. See \"notmuch help hooks\" for\n" - "\tmore details on hooks.\n" - "\n" - "\tSupported options for new include:\n" - "\n" - "\t--no-hooks\n" - "\n" - "\t\tPrevent hooks from being run.\n" - "\n" - "\t--verbose\n" - "\n" - "\t\tVerbose operation. Shows paths of message files as\n" - "\t\tthey are being indexed.\n" - "\n" - "\tInvoking notmuch with no command argument will run new if\n" - "\tthe setup command has previously been completed, but new has\n" - "\tnot previously been run." }, + "Find and import new messages to the notmuch database." }, { "search", notmuch_search_command, "[options...] [...]", - "Search for messages matching the given search terms.", - "\tNote that the individual mail messages will be matched\n" - "\tagainst the search terms, but the results will be the\n" - "\tthreads (one per line) containing the matched messages.\n" - "\n" - "\tSupported options for search include:\n" - "\n" - "\t--format=(json|text)\n" - "\n" - "\t\tPresents the results in either JSON or\n" - "\t\tplain-text (default)\n" - "\n" - "\t--output=(summary|threads|messages|files|tags)\n" - "\n" - "\t\tsummary (default)\n" - "\n" - "\t\tOutput a summary of each thread with any message matching the\n" - "\t\tsearch terms. The summary includes the thread ID, date, the\n" - "\t\tnumber of messages in the thread (both the number matched and\n" - "\t\tthe total number), the authors of the thread and the subject.\n" - "\n" - "\t\tthreads\n" - "\n" - "\t\tOutput the thread IDs of all threads with any message matching\n" - "\t\tthe search terms, either one per line (--format=text) or as a\n" - "\t\tJSON array (--format=json).\n" - "\n" - "\t\tmessages\n" - "\n" - "\t\tOutput the message IDs of all messages matching the search\n" - "\t\tterms, either one per line (--format=text) or as a JSON array\n" - "\t\t(--format=json).\n" - "\n" - "\t\tfiles\n" - "\n" - "\t\tOutput the filenames of all messages matching the search\n" - "\t\tterms, either one per line (--format=text) or as a JSON array\n" - "\t\t(--format=json).\n" - "\n" - "\t\ttags\n" - "\n" - "\t\tOutput all tags that appear on any message matching the search\n" - "\t\tterms, either one per line (--format=text) or as a JSON array\n" - "\t\t(--format=json).\n" - "\n" - "\t--sort=(newest-first|oldest-first)\n" - "\n" - "\t\tPresent results in either chronological order\n" - "\t\t(oldest-first) or reverse chronological order\n" - "\t\t(newest-first), which is the default.\n" - "\n" - "\t--offset=[-]N\n" - "\n" - "\t\tSkip displaying the first N results. With the leading '-',\n" - "\t\tstart at the Nth result from the end.\n" - "\n" - "\t--limit=N\n" - "\n" - "\t\tLimit the number of displayed results to N.\n" - "\n" - "\tSee \"notmuch help search-terms\" for details of the search\n" - "\tterms syntax." }, + "Search for messages matching the given search terms." }, { "show", notmuch_show_command, " [...]", - "Show all messages matching the search terms.", - "\tThe messages are grouped and sorted based on the threading\n" - "\t(all replies to a particular message appear immediately\n" - "\tafter that message in date order).\n" - "\n" - "\tSupported options for show include:\n" - "\n" - "\t--entire-thread\n" - "\n" - "\t\tBy default only those messages that match the\n" - "\t\tsearch terms will be displayed. With this option,\n" - "\t\tall messages in the same thread as any matched\n" - "\t\tmessage will be displayed.\n" - "\n" - "\t--format=(text|json|mbox|raw)\n" - "\n" - "\t\ttext (default for messages)\n" - "\n" - "\t\tThe default plain-text format has all text-content MIME parts\n" - "\t\tdecoded. Various components in the output, ('message', 'header',\n" - "\t\t'body', 'attachment', and MIME 'part') are delimited by\n" - "\t\teasily-parsed markers. Each marker consists of a Control-L\n" - "\t\tcharacter (ASCII decimal 12), the name of the marker, and\n" - "\t\tthen either an opening or closing brace, '{' or '}' to\n" - "\t\teither open or close the component. For a multipart MIME\n" - "\t\tmessage, these parts will be nested.\n" - "\n" - "\t\tjson\n" - "\n" - "\t\tThe output is formatted with Javascript Object Notation\n" - "\t\t(JSON). This format is more robust than the text format\n" - "\t\tfor automated processing. The nested structure of multipart\n" - "\t\tMIME messages is reflected in nested JSON output. JSON\n" - "\t\toutput always includes all messages in a matching thread;\n" - "\t\tin effect '--format=json' implies '--entire-thread'\n" - "\n" - "\t\tmbox\n" - "\n" - "\t\tAll matching messages are output in the traditional, Unix\n" - "\t\tmbox format with each message being prefixed by a line\n" - "\t\tbeginning with 'From ' and a blank line separating each\n" - "\t\tmessage. Lines in the message content beginning with 'From '\n" - "\t\t(preceded by zero or more '>' characters) have an additional\n" - "\t\t'>' character added. This reversible escaping is termed\n" - "\t\t\"mboxrd\" format and described in detail here:\n" - "\n" - "\t\thttp://homepage.ntlworld.com/jonathan.deboynepollard/FGA/mail-mbox-formats.html\n" - "\n" - "\t\traw (default for a single part, see --part)\n" - "\n" - "\t\tFor a message, the original, raw content of the email\n" - "\t\tmessage is output. Consumers of this format should\n" - "\t\texpect to implement MIME decoding and similar functions.\n" - "\n" - "\t\tFor a single part (--part) the raw part content is output\n" - "\t\tafter performing any necessary MIME decoding.\n" - "\n" - "\t\tThe raw format must only be used with search terms matching\n" - "\t\tsingle message.\n" - "\n" - "\t--part=N\n" - "\n" - "\t\tOutput the single decoded MIME part N of a single message.\n" - "\t\tThe search terms must match only a single message.\n" - "\t\tMessage parts are numbered in a depth-first walk of the\n" - "\t\tmessage MIME structure, and are identified in the 'json' or\n" - "\t\t'text' output formats.\n" - "\n" - "\t--verify\n" - "\n" - "\t\tCompute and report the validity of any MIME cryptographic\n" - "\t\tsignatures found in the selected content (ie.\n" - "\t\t\"multipart/signed\" parts). Status of the signature will be\n" - "\t\treported (currently only supported with --format=json) and\n" - "\t\tthe multipart/signed part will be replaced by the signed data.\n" - "\n" - "\t--decrypt\n" - "\n" - "\t\tDecrypt any MIME encrypted parts found in the selected content\n" - "\t\t(ie. \"multipart/encrypted\" parts). Status of the decryption\n" - "\t\twill be reported (currently only supported with --format=json)\n" - "\t\tand the multipart/encrypted part will be replaced by the\n" - "\t\tdecrypted content.\n" - "\n" - "\n" - "\tA common use of \"notmuch show\" is to display a single\n" - "\tthread of email messages. For this, use a search term of\n" - "\t\"thread:\" as can be seen in the first column\n" - "\tof output from the \"notmuch search\" command.\n" - "\n" - "\tSee \"notmuch help search-terms\" for details of the search\n" - "\tterms syntax." }, + "Show all messages matching the search terms." }, { "count", notmuch_count_command, "[options...] [...]", - "Count messages matching the search terms.", - "\tThe number of matching messages (or threads) is output to stdout.\n" - "\n" - "\tWith no search terms, a count of all messages (or threads) in\n" - "\tthe database will be displayed.\n" - "\n" - "\tSupported options for count include:\n" - "\n" - "\t--output=(messages|threads)\n" - "\n" - "\t\tmessages (default)\n" - "\n" - "\t\tOutput the number of matching messages.\n" - "\n" - "\t\tthreads\n" - "\n" - "\t\tOutput the number of matching threads.\n" - "\n" - "\tSee \"notmuch help search-terms\" for details of the search\n" - "\tterms syntax." }, + "Count messages matching the search terms." }, { "reply", notmuch_reply_command, "[options...] [...]", - "Construct a reply template for a set of messages.", - "\tConstructs a new message as a reply to a set of existing\n" - "\tmessages. The Reply-To: header (if any, otherwise From:) is\n" - "\tused for the To: address. The To: and Cc: headers are copied,\n" - "\tbut not including any of the user's configured addresses.\n" - "\n" - "\tA suitable subject is constructed. The In-Reply-to: and\n" - "\tReferences: headers are set appropriately, and the content\n" - "\tof the original messages is quoted and included in the body\n" - "\t(unless --format=headers-only is given).\n" - "\n" - "\tThe resulting message template is output to stdout.\n" - "\n" - "\tSupported options for reply include:\n" - "\n" - "\t--format=(default|headers-only)\n" - "\n" - "\t\tdefault:\n" - "\t\t\tIncludes subject and quoted message body.\n" - "\n" - "\t\theaders-only:\n" - "\t\t\tOnly produces In-Reply-To, References, To\n" - "\t\t\tCc, and Bcc headers.\n" - "\n" - "\tSee \"notmuch help search-terms\" for details of the search\n" - "\tterms syntax." }, + "Construct a reply template for a set of messages." }, { "tag", notmuch_tag_command, - "+|- [...] [--] [...]", - "Add/remove tags for all messages matching the search terms.", - "\tThe search terms are handled exactly as in 'search' so one\n" - "\tcan use that command first to see what will be modified.\n" - "\n" - "\tTags prefixed by '+' are added while those prefixed by\n" - "\t'-' are removed. For each message, tag removal is performed\n" - "\tbefore tag addition.\n" - "\n" - "\tThe beginning of is recognized by the first\n" - "\targument that begins with neither '+' nor '-'. Support for\n" - "\tan initial search term beginning with '+' or '-' is provided\n" - "\tby allowing the user to specify a \"--\" argument to separate\n" - "\tthe tags from the search terms.\n" - "\n" - "\tSee \"notmuch help search-terms\" for details of the search\n" - "\tterms syntax." }, + "+|- [...] [--] [...]" , + "Add/remove tags for all messages matching the search terms." }, { "dump", notmuch_dump_command, "[] [--] []", - "Create a plain-text dump of the tags for each message.", - "\tOutput is to the given filename, if any, or to stdout.\n" - "\tNote that using the filename argument is deprecated.\n" - "\n" - "\tThese tags are the only data in the notmuch database\n" - "\tthat can't be recreated from the messages themselves.\n" - "\tThe output of notmuch dump is therefore the only\n" - "\tcritical thing to backup (and much more friendly to\n" - "\tincremental backup than the native database files.)\n" - "\n" - "\tWith no search terms, a dump of all messages in the\n" - "\tdatabase will be generated. A \"--\" argument instructs\n" - "\tnotmuch that the remaining arguments are search terms.\n" - "\n" - "\tSee \"notmuch help search-terms\" for the search-term syntax.\n" - }, + "Create a plain-text dump of the tags for each message." }, { "restore", notmuch_restore_command, "[--accumulate] []", - "Restore the tags from the given dump file (see 'dump').", - "\tInput is read from the given filename, if any, or from stdin.\n" - "\tNote: The dump file format is specifically chosen to be\n" - "\tcompatible with the format of files produced by sup-dump.\n" - "\tSo if you've previously been using sup for mail, then the\n" - "\t\"notmuch restore\" command provides you a way to import\n" - "\tall of your tags (or labels as sup calls them).\n" - "\tThe --accumulate switch causes the union of the existing and new\n" - "\ttags to be applied, instead of replacing each message's tags as\n" - "\tthey are read in from the dump file."}, + "Restore the tags from the given dump file (see 'dump')." }, { "config", notmuch_config_command, "[get|set]
. [value ...]", - "Get or set settings in the notmuch configuration file.", - " config get
.\n" - "\n" - "\tThe value of the specified configuration item is printed\n" - "\tto stdout. If the item has multiple values, each value\n" - "\tis separated by a newline character.\n" - "\n" - "\tAvailable configuration items include at least\n" - "\n" - "\t\tdatabase.path\n" - "\t\tuser.name\n" - "\t\tuser.primary_email\n" - "\t\tuser.other_email\n" - "\t\tnew.tags\n" - "\n" - " config set
. [value ...]\n" - "\n" - "\tThe specified configuration item is set to the given value.\n" - "\tTo specify a multiple-value item, provide each value as\n" - "\ta separate command-line argument.\n" - "\n" - "\tIf no values are provided, the specified configuration item\n" - "\twill be removed from the configuration file." }, + "Get or set settings in the notmuch configuration file." }, { "help", notmuch_help_command, "[]", - "This message, or more detailed help for the named command.", - "\tExcept in this case, where there's not much more detailed\n" - "\thelp available." } + "This message, or more detailed help for the named command." } }; static void @@ -517,8 +109,17 @@ usage (FILE *out) "and \"notmuch help search-terms\" for the common search-terms syntax.\n\n"); } +static void +exec_man (const char *page) +{ + if (execlp ("man", "man", page, (char *) NULL)) { + perror ("exec man"); + exit (1); + } +} + static int -notmuch_help_command (unused (void *ctx), int argc, char *argv[]) +notmuch_help_command (void *ctx, int argc, char *argv[]) { command_t *command; unsigned int i; @@ -531,41 +132,28 @@ notmuch_help_command (unused (void *ctx), int argc, char *argv[]) return 0; } + if (strcmp (argv[0], "help") == 0) { + printf ("The notmuch help system.\n\n" + "\tNotmuch uses the man command to display help. In case\n" + "\tof difficulties check that MANPATH includes the pages\n" + "\tinstalled by notmuch.\n\n" + "\tTry \"notmuch help\" for a list of topics.\n"); + return 0; + } + for (i = 0; i < ARRAY_SIZE (commands); i++) { command = &commands[i]; if (strcmp (argv[0], command->name) == 0) { - printf ("Help for \"notmuch %s\":\n\n", argv[0]); - if (command->arguments) - printf ("%s %s\n\n\t%s\n\n%s\n\n", - command->name, command->arguments, - command->summary, command->documentation); - else - printf ("%s\t%s\n\n%s\n\n", command->name, - command->summary, command->documentation); - return 0; + char *page = talloc_asprintf (ctx, "notmuch-%s", command->name); + exec_man (page); } } if (strcmp (argv[0], "search-terms") == 0) { - printf ("Help for <%s>\n\n", argv[0]); - for (i = 0; i < ARRAY_SIZE (commands); i++) { - command = &commands[i]; - - if (command->arguments && - strstr (command->arguments, "search-terms")) - { - printf ("\t%s\t%s\n", - command->name, command->arguments); - } - } - printf ("\n"); - printf (search_terms_help); - return 0; + exec_man ("notmuch-search-terms"); } else if (strcmp (argv[0], "hooks") == 0) { - printf ("Help for <%s>\n\n", argv[0]); - printf (hooks_help); - return 0; + exec_man ("notmuch-hooks"); } fprintf (stderr, diff --git a/show-message.c b/show-message.c index d83f04ec..8768889e 100644 --- a/show-message.c +++ b/show-message.c @@ -24,203 +24,79 @@ typedef struct show_message_state { int part_count; - int in_zone; } show_message_state_t; static void -show_message_part (GMimeObject *part, +show_message_part (mime_node_t *node, show_message_state_t *state, const notmuch_show_format_t *format, - notmuch_show_params_t *params, int first) { - GMimeObject *decryptedpart = NULL; - int selected; - state->part_count += 1; + /* Formatters expect the envelope for embedded message parts */ + GMimeObject *part = node->envelope_part ? + GMIME_OBJECT (node->envelope_part) : node->part; + int i; - if (! (GMIME_IS_PART (part) || GMIME_IS_MULTIPART (part) || GMIME_IS_MESSAGE_PART (part))) { - fprintf (stderr, "Warning: Not displaying unknown mime part: %s.\n", - g_type_name (G_OBJECT_TYPE (part))); - return; - } + if (!first) + fputs (format->part_sep, stdout); - selected = (params->part <= 0 || state->part_count == params->part); + /* Format this part */ + if (format->part_start) + format->part_start (part, &(state->part_count)); - if (selected || state->in_zone) { - if (!first && (params->part <= 0 || state->in_zone)) - fputs (format->part_sep, stdout); + if (node->decrypt_attempted && format->part_encstatus) + format->part_encstatus (node->decrypt_success); - if (format->part_start) - format->part_start (part, &(state->part_count)); - } + if (node->verify_attempted && format->part_sigstatus) + format->part_sigstatus (node->sig_validity); - /* handle PGP/MIME parts */ - if (GMIME_IS_MULTIPART (part) && params->cryptoctx) { - GMimeMultipart *multipart = GMIME_MULTIPART (part); - GError* err = NULL; - - if (GMIME_IS_MULTIPART_ENCRYPTED (part) && params->decrypt) - { - if ( g_mime_multipart_get_count (multipart) != 2 ) { - /* this violates RFC 3156 section 4, so we won't bother with it. */ - fprintf (stderr, - "Error: %d part(s) for a multipart/encrypted message (should be exactly 2)\n", - g_mime_multipart_get_count (multipart)); - } else { - GMimeMultipartEncrypted *encrypteddata = GMIME_MULTIPART_ENCRYPTED (part); - decryptedpart = g_mime_multipart_encrypted_decrypt (encrypteddata, params->cryptoctx, &err); - if (decryptedpart) { - if ((selected || state->in_zone) && format->part_encstatus) - format->part_encstatus (1); - const GMimeSignatureValidity *sigvalidity = g_mime_multipart_encrypted_get_signature_validity (encrypteddata); - if (!sigvalidity) - fprintf (stderr, "Failed to verify signed part: %s\n", (err ? err->message : "no error explanation given")); - if ((selected || state->in_zone) && format->part_sigstatus) - format->part_sigstatus (sigvalidity); - } else { - fprintf (stderr, "Failed to decrypt part: %s\n", (err ? err->message : "no error explanation given")); - if ((selected || state->in_zone) && format->part_encstatus) - format->part_encstatus (0); - } - } - } - else if (GMIME_IS_MULTIPART_SIGNED (part)) - { - if ( g_mime_multipart_get_count (multipart) != 2 ) { - /* this violates RFC 3156 section 5, so we won't bother with it. */ - fprintf (stderr, - "Error: %d part(s) for a multipart/signed message (should be exactly 2)\n", - g_mime_multipart_get_count (multipart)); - } else { - /* For some reason the GMimeSignatureValidity returned - * here is not a const (inconsistent with that - * returned by - * g_mime_multipart_encrypted_get_signature_validity, - * and therefore needs to be properly disposed of. - * Hopefully the API will become more consistent. */ - GMimeSignatureValidity *sigvalidity = g_mime_multipart_signed_verify (GMIME_MULTIPART_SIGNED (part), params->cryptoctx, &err); - if (!sigvalidity) { - fprintf (stderr, "Failed to verify signed part: %s\n", (err ? err->message : "no error explanation given")); - } - if ((selected || state->in_zone) && format->part_sigstatus) - format->part_sigstatus (sigvalidity); - if (sigvalidity) - g_mime_signature_validity_free (sigvalidity); - } - } - - if (err) - g_error_free (err); - } - /* end handle PGP/MIME parts */ - - if (selected || state->in_zone) - format->part_content (part); - - if (GMIME_IS_MULTIPART (part)) { - GMimeMultipart *multipart = GMIME_MULTIPART (part); - int i; - - if (selected) - state->in_zone = 1; - - if (decryptedpart) { - /* We emit the useless application/pgp-encrypted version - * part here only to keep the emitted output as consistent - * as possible between decrypted output and the - * unprocessed multipart/mime. For some strange reason, - * the actual encrypted data is the second part of the - * multipart. */ - show_message_part (g_mime_multipart_get_part (multipart, 0), state, format, params, TRUE); - show_message_part (decryptedpart, state, format, params, FALSE); - } else { - for (i = 0; i < g_mime_multipart_get_count (multipart); i++) { - show_message_part (g_mime_multipart_get_part (multipart, i), - state, format, params, i == 0); - } - } - - if (selected) - state->in_zone = 0; - - } else if (GMIME_IS_MESSAGE_PART (part)) { - GMimeMessage *mime_message = g_mime_message_part_get_message (GMIME_MESSAGE_PART (part)); - - if (selected) - state->in_zone = 1; - - if (selected || (!selected && state->in_zone)) { - fputs (format->header_start, stdout); - if (format->header_message_part) - format->header_message_part (mime_message); - fputs (format->header_end, stdout); - - fputs (format->body_start, stdout); - } - - show_message_part (g_mime_message_get_mime_part (mime_message), - state, format, params, TRUE); - - if (selected || (!selected && state->in_zone)) - fputs (format->body_end, stdout); - - if (selected) - state->in_zone = 0; - } + format->part_content (part); - if (selected || state->in_zone) { - if (format->part_end) - format->part_end (part); + if (node->envelope_part) { + fputs (format->header_start, stdout); + if (format->header_message_part) + format->header_message_part (GMIME_MESSAGE (node->part)); + fputs (format->header_end, stdout); + + fputs (format->body_start, stdout); } + + /* Recurse over the children */ + state->part_count += 1; + for (i = 0; i < node->nchildren; i++) + show_message_part (mime_node_child (node, i), state, format, i == 0); + + /* Finish this part */ + if (node->envelope_part) + fputs (format->body_end, stdout); + + if (format->part_end) + format->part_end (part); } notmuch_status_t -show_message_body (const char *filename, +show_message_body (notmuch_message_t *message, const notmuch_show_format_t *format, notmuch_show_params_t *params) { - GMimeStream *stream = NULL; - GMimeParser *parser = NULL; - GMimeMessage *mime_message = NULL; - notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS; - FILE *file = NULL; + notmuch_status_t ret; show_message_state_t state; + mime_node_t *root, *part; - state.part_count = 0; - state.in_zone = 0; - - file = fopen (filename, "r"); - if (! file) { - fprintf (stderr, "Error opening %s: %s\n", filename, strerror (errno)); - ret = NOTMUCH_STATUS_FILE_ERROR; - goto DONE; - } - - stream = g_mime_stream_file_new (file); - g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream), FALSE); - - parser = g_mime_parser_new_with_stream (stream); - - mime_message = g_mime_parser_construct_message (parser); - - show_message_part (g_mime_message_get_mime_part (mime_message), - &state, - format, - params, - TRUE); - - DONE: - if (mime_message) - g_object_unref (mime_message); + ret = mime_node_open (NULL, message, params->cryptoctx, params->decrypt, + &root); + if (ret) + return ret; - if (parser) - g_object_unref (parser); + /* The caller of show_message_body has already handled the + * outermost envelope, so skip it. */ + state.part_count = MAX (params->part, 1); - if (stream) - g_object_unref (stream); + part = mime_node_seek_dfs (root, state.part_count); + if (part) + show_message_part (part, &state, format, TRUE); - if (file) - fclose (file); + talloc_free (root); - return ret; + return NOTMUCH_STATUS_SUCCESS; } diff --git a/test/README b/test/README index 7b2e96d4..bde6db0c 100644 --- a/test/README +++ b/test/README @@ -56,6 +56,13 @@ can be specified as follows: make test OPTIONS="--verbose" +You can choose an emacs binary to run the tests in one of the +following ways. + + TEST_EMACS=my-special-emacs make test + TEST_EMACS=my-special-emacs ./emacs + make test TEST_EMACS=my-special-emacs + Skipping Tests -------------- If, for any reason, you need to skip one or more tests, you can do so diff --git a/test/corpus/cur/52:2, b/test/corpus/cur/52:2, new file mode 100644 index 00000000..60283404 --- /dev/null +++ b/test/corpus/cur/52:2, @@ -0,0 +1,39 @@ +Message-ID: <4EFC743A.3060609@april.org> +Date: Thu, 29 Dec 2010 15:07:54 +0100 +From: "=?ISO-8859-1?Q?Fran=E7ois_Boulogne?=" +User-Agent: Mozilla/5.0 (X11; Linux i686; + rv:9.0) Gecko/20111224 Thunderbird/9.0.1 +MIME-Version: 1.0 +To: Allan McRae , + "Discussion about the Arch User Repository (AUR)" +References: <4EFC3931.6030007@april.org> <4EFC3D62.4030202@archlinux.org> +In-Reply-To: <4EFC3D62.4030202@archlinux.org> +Content-Type: text/plain; charset=ISO-8859-1 +Content-Transfer-Encoding: 8bit +Subject: Re: [aur-general] Guidelines: cp, mkdir vs install + +Le 29/12/2011 11:13, Allan McRae a écrit : +> On 29/12/11 19:56, François Boulogne wrote: +>> Hi, +>> +>> Looking to improve the quality of my packages, I read again the guidelines. +>> https://wiki.archlinux.org/index.php/Arch_Packaging_Standards +>> +>> However, it don't see anything about the install command like +>> install -d $pkgdir/usr/{bin,share/man/man1,share/locale} +>> +>> Some contributors on AUR use cp or mkdir to install files/dir (when no +>> makefile is provided) and others use install command. +>> +>> What's the opinion of TU on this point? +>> +> +> Use install with -m specifying the correct permissions +> + +Thank you Allan + + +-- +François Boulogne. +https://www.sciunto.org diff --git a/test/corpus/cur/53:2, b/test/corpus/cur/53:2, new file mode 100644 index 00000000..7a1e2e58 --- /dev/null +++ b/test/corpus/cur/53:2, @@ -0,0 +1,20 @@ +From: Olivier Berger +To: olivier.berger@it-sudparis.eu +Subject: Essai =?iso-8859-1?Q?accentu=E9?= +User-Agent: Notmuch/0.10.1 (http://notmuchmail.org) Emacs/23.3.1 (i486-pc-linux-gnu) +X-Draft-From: ("nnimap+localdovecot:INBOX" 44228) +Date: Fri, 16 Dec 2010 16:49:59 +0100 +Message-ID: <877h1wv7mg.fsf@inf-8657.int-evry.fr> +MIME-Version: 1.0 +Content-Type: text/plain; charset=iso-8859-1 +Content-Transfer-Encoding: quoted-printable + +Du texte accentu=E9 pour =E7a ... + +=E0 la bonne heure ! +--=20 +Olivier BERGER=20 +http://www-public.it-sudparis.eu/~berger_o/ - OpenPGP-Id: 2048R/5819D7E8 +Ingenieur Recherche - Dept INF +Institut TELECOM, SudParis (http://www.it-sudparis.eu/), Evry (France) + diff --git a/test/emacs b/test/emacs index f36718e7..ac47b161 100755 --- a/test/emacs +++ b/test/emacs @@ -81,7 +81,7 @@ cat <EXPECTED "Invalid " From" (2001-01-05) (inbox) Subject: message-with-invalid-from To: Notmuch Test Suite -Date: Tue, 05 Jan 2001 15:43:57 -0000 +Date: Fri, 05 Jan 2001 15:43:57 +0000 This is just a test message (#1) EOF @@ -320,7 +320,7 @@ add_message '[from]="Top Poster "' \ ----- Original Message ----- From: Notmuch Test Suite To: Notmuch Test Suite -Sent: Tue, 05 Jan 2001 15:43:57 -0000 +Sent: Fri, 05 Jan 2001 15:43:57 +0000 Subject: The problem with top-posting Q: Why is top-posting such a bad thing? @@ -331,7 +331,7 @@ test_emacs "(notmuch-show \"top-posting\") echo "Notmuch Test Suite (2001-01-05) (inbox) Subject: The problem with top-posting To: Notmuch Test Suite -Date: Tue, 05 Jan 2001 15:43:57 -0000 +Date: Fri, 05 Jan 2001 15:43:57 +0000 A: Because it messes up the order in which people normally read text. Q: Why is top-posting such a bad thing? @@ -340,7 +340,7 @@ Q: What is the most annoying thing in e-mail? Top Poster (2001-01-05) (inbox unread) Subject: Re: The problem with top-posting To: Notmuch Test Suite -Date: Tue, 05 Jan 2001 15:43:57 -0000 +Date: Fri, 05 Jan 2001 15:43:57 +0000 Thanks for the advice! I will be sure to put it to good use. diff --git a/test/emacs-subject-to-filename b/test/emacs-subject-to-filename new file mode 100755 index 00000000..176e6859 --- /dev/null +++ b/test/emacs-subject-to-filename @@ -0,0 +1,138 @@ +#!/usr/bin/env bash + +test_description="emacs: mail subject to filename" +. test-lib.sh + +# emacs server can't be started in a child process with $(test_emacs ...) +test_emacs '(ignore)' + +# test notmuch-wash-subject-to-patch-sequence-number (subject) +test_begin_subtest "no patch sequence number" +output=$(test_emacs '(notmuch-wash-subject-to-patch-sequence-number + "[PATCH] A normal patch subject without numbers")' +) +test_expect_equal "$output" "" + +test_begin_subtest "patch sequence number #1" +output=$(test_emacs '(notmuch-wash-subject-to-patch-sequence-number + "[PATCH 2/3] A most regular patch subject")' +) +test_expect_equal "$output" 2 + +test_begin_subtest "patch sequence number #2" +output=$(test_emacs '(notmuch-wash-subject-to-patch-sequence-number + " [dummy list prefix] [RFC PATCH v2 13/42] Special prefixes")' +) +test_expect_equal "$output" 13 + +test_begin_subtest "patch sequence number #3" +output=$(test_emacs '(notmuch-wash-subject-to-patch-sequence-number + "[PATCH 2/3] [PATCH 032/037] use the last prefix")' +) +test_expect_equal "$output" 32 + +test_begin_subtest "patch sequence number #4" +output=$(test_emacs '(notmuch-wash-subject-to-patch-sequence-number + "[dummy list prefix] [PATCH 2/3] PATCH 3/3] do not use a broken prefix")' +) +test_expect_equal "$output" 2 + +test_begin_subtest "patch sequence number #5" +output=$(test_emacs '(notmuch-wash-subject-to-patch-sequence-number + "[RFC][PATCH 3/5][PATCH 4/5][PATCH 5/5] A made up test")' +) +test_expect_equal "$output" 5 + +test_begin_subtest "patch sequence number #6" +output=$(test_emacs '(notmuch-wash-subject-to-patch-sequence-number + "[PATCH 2/3] this -> [PATCH 3/3] is not a prefix anymore [nor this 4/4]")' +) +test_expect_equal "$output" 2 + +test_begin_subtest "patch sequence number #7" +output=$(test_emacs '(notmuch-wash-subject-to-patch-sequence-number + "[liberally accept crapola right before123/456and after] the numbers")' +) +test_expect_equal "$output" 123 + +# test notmuch-wash-subject-to-filename (subject &optional maxlen) +test_begin_subtest "filename #1" +output=$(test_emacs '(notmuch-wash-subject-to-filename + "just a subject line")' +) +test_expect_equal $output '"just-a-subject-line"' + +test_begin_subtest "filename #2" +output=$(test_emacs '(notmuch-wash-subject-to-filename + " [any] [prefixes are ] [removed!] from the subject")' +) +test_expect_equal $output '"from-the-subject"' + +test_begin_subtest "filename #3" +output=$(test_emacs '(notmuch-wash-subject-to-filename + " leading and trailing space ")' +) +test_expect_equal $output '"leading-and-trailing-space"' + +test_begin_subtest "filename #4" +output=$(test_emacs '(notmuch-wash-subject-to-filename + "!# leading ()// &%, and in between_and_trailing garbage ()(&%%")' +) +test_expect_equal $output '"-leading-and-in-between_and_trailing-garbage"' + +test_begin_subtest "filename #5" +output=$(test_emacs '(notmuch-wash-subject-to-filename + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_01234567890")' +) +test_expect_equal $output '"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-_01234567890"' + +test_begin_subtest "filename #6" +output=$(test_emacs '(notmuch-wash-subject-to-filename + "sequences of ... are squashed and trailing are removed ...")' +) +test_expect_equal $output '"sequences-of-.-are-squashed-and-trailing-are-removed"' + +test_begin_subtest "filename #7" +output=$(test_emacs '(notmuch-wash-subject-to-filename + "max length test" 1)' +) +test_expect_equal $output '"m"' + +test_begin_subtest "filename #8" +output=$(test_emacs '(notmuch-wash-subject-to-filename + "max length test /&(/%&/%%&¤%¤" 20)' +) +test_expect_equal $output '"max-length-test"' + +test_begin_subtest "filename #9" +output=$(test_emacs '(notmuch-wash-subject-to-filename + "[a prefix] [is only separated] by [spaces], so \"by\" is not okay!")' +) +test_expect_equal $output '"by-spaces-so-by-is-not-okay"' + +# test notmuch-wash-subject-to-patch-filename (subject) +test_begin_subtest "patch filename #1" +output=$(test_emacs '(notmuch-wash-subject-to-patch-filename + "[RFC][PATCH 099/100] rewrite notmuch")' +) +test_expect_equal "$output" '"0099-rewrite-notmuch.patch"' + +test_begin_subtest "patch filename #2" +output=$(test_emacs '(notmuch-wash-subject-to-patch-filename + "[RFC PATCH v1] has no patch number, default to 1")' +) +test_expect_equal "$output" '"0001-has-no-patch-number-default-to-1.patch"' + +test_begin_subtest "patch filename #3" +output=$(test_emacs '(notmuch-wash-subject-to-patch-filename + "[PATCH 4/5] the maximum length of a patch filename is 52 + patch sequence number + .patch extension")' +) +test_expect_equal "$output" '"0004-the-maximum-length-of-a-patch-filename-is-52-patch-s.patch"' + +test_begin_subtest "patch filename #4" +output=$(test_emacs '(notmuch-wash-subject-to-patch-filename + "[PATCH 4/5] the maximum length of a patch filename is 52 + patchh ! sequence number + .patch extension, *before* trimming trailing - and .")' +) +test_expect_equal "$output" '"0004-the-maximum-length-of-a-patch-filename-is-52-patchh.patch"' + +test_done diff --git a/test/emacs.expected-output/notmuch-hello b/test/emacs.expected-output/notmuch-hello index 48143bd7..de57de25 100644 --- a/test/emacs.expected-output/notmuch-hello +++ b/test/emacs.expected-output/notmuch-hello @@ -1,8 +1,8 @@ - Welcome to notmuch. You have 50 messages. + Welcome to notmuch. You have 52 messages. Saved searches: [edit] - 50 inbox 50 unread + 52 inbox 52 unread Search: diff --git a/test/emacs.expected-output/notmuch-hello-no-saved-searches b/test/emacs.expected-output/notmuch-hello-no-saved-searches index 7c09e40b..f1fc4d6a 100644 --- a/test/emacs.expected-output/notmuch-hello-no-saved-searches +++ b/test/emacs.expected-output/notmuch-hello-no-saved-searches @@ -1,4 +1,4 @@ - Welcome to notmuch. You have 50 messages. + Welcome to notmuch. You have 52 messages. Search: diff --git a/test/emacs.expected-output/notmuch-hello-view-inbox b/test/emacs.expected-output/notmuch-hello-view-inbox index 894ae5fa..1688d674 100644 --- a/test/emacs.expected-output/notmuch-hello-view-inbox +++ b/test/emacs.expected-output/notmuch-hello-view-inbox @@ -20,4 +20,6 @@ 2009-11-18 [1/1] Alexander Botero-Lowry [notmuch] request for pull (inbox unread) 2009-11-18 [2/2] Keith Packard, Alexander Botero-Lowry [notmuch] [PATCH] Create a default notmuch-show-hook that highlights URLs and uses word-wrap (inbox unread) 2009-11-18 [1/1] Chris Wilson [notmuch] [PATCH 1/2] Makefile: evaluate pkg-config once (inbox unread) + 2010-12-16 [1/1] Olivier Berger Essai accentué (inbox unread) + 2010-12-29 [1/1] François Boulogne [aur-general] Guidelines: cp, mkdir vs install (inbox unread) End of search results. diff --git a/test/emacs.expected-output/notmuch-hello-with-empty b/test/emacs.expected-output/notmuch-hello-with-empty index 2a267c92..dd8728b1 100644 --- a/test/emacs.expected-output/notmuch-hello-with-empty +++ b/test/emacs.expected-output/notmuch-hello-with-empty @@ -1,8 +1,8 @@ - Welcome to notmuch. You have 50 messages. + Welcome to notmuch. You have 52 messages. Saved searches: [edit] - 50 inbox 50 unread 0 empty + 52 inbox 52 unread 0 empty Search: diff --git a/test/emacs.expected-output/notmuch-search-tag-inbox b/test/emacs.expected-output/notmuch-search-tag-inbox index 9456ccfd..8a53555a 100644 --- a/test/emacs.expected-output/notmuch-search-tag-inbox +++ b/test/emacs.expected-output/notmuch-search-tag-inbox @@ -1,3 +1,5 @@ + 2010-12-29 [1/1] François Boulogne [aur-general] Guidelines: cp, mkdir vs install (inbox unread) + 2010-12-16 [1/1] Olivier Berger Essai accentué (inbox unread) 2009-11-18 [1/1] Chris Wilson [notmuch] [PATCH 1/2] Makefile: evaluate pkg-config once (inbox unread) 2009-11-18 [2/2] Alex Botero-Lowry, Carl Worth [notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop (attachment inbox unread) 2009-11-18 [2/2] Ingmar Vanhassel, Carl Worth [notmuch] [PATCH] Typsos (inbox unread) diff --git a/test/emacs.expected-output/notmuch-show-thread-maildir-storage b/test/emacs.expected-output/notmuch-show-thread-maildir-storage index 8ba64b27..cdbfa1d7 100644 --- a/test/emacs.expected-output/notmuch-show-thread-maildir-storage +++ b/test/emacs.expected-output/notmuch-show-thread-maildir-storage @@ -26,12 +26,11 @@ with Maildir) or if something else is going on. Cheers, -[ 5-line signature. Click/Enter to show. ] +[ 4-line signature. Click/Enter to show. ] -- Lars Kellogg-Stedman Senior Technologist, Computing and Information Technology Harvard University School of Engineering and Applied Sciences - [ application/pgp-signature ] [ text/plain ] [ 4-line signature. Click/Enter to show. ] @@ -48,12 +47,14 @@ http://notmuchmail.org/mailman/listinfo/notmuch [ multipart/signed ] [ 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: LK> Error opening - LK> /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S: + LK> + /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S: LK> Too many open files See the patch just posted here. @@ -88,12 +89,11 @@ http://notmuchmail.org/mailman/listinfo/notmuch -- Lars - [ 5-line signature. Click/Enter to show. ] + [ 4-line signature. Click/Enter to show. ] -- Lars Kellogg-Stedman Senior Technologist, Computing and Information Technology Harvard University School of Engineering and Applied Sciences - [ application/pgp-signature ] [ text/plain ] [ 4-line signature. Click/Enter to show. ] @@ -106,8 +106,8 @@ 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 @@ -127,14 +127,15 @@ http://notmuchmail.org/mailman/listinfo/notmuch Type: application/pgp-signature Size: 834 bytes Desc: not available - URL: - + URL: + Keith Packard (2009-11-17) (inbox unread) Subject: [notmuch] Working with Maildir storage? To: notmuch@notmuchmail.org Date: Tue, 17 Nov 2009 13:24:13 -0800 - On Tue, 17 Nov 2009 15:33:01 -0500, Lars Kellogg-Stedman wrote: + On Tue, 17 Nov 2009 15:33:01 -0500, Lars Kellogg-Stedman wrote: > > See the patch just posted here. I've also pushed a slightly more complicated (and complete) fix to my @@ -150,7 +151,6 @@ http://notmuchmail.org/mailman/listinfo/notmuch Thanks to everyone for trying out notmuch! -keith - Lars Kellogg-Stedman (2009-11-18) (inbox signed unread) Subject: Re: [notmuch] Working with Maildir storage? To: Keith Packard @@ -166,12 +166,11 @@ http://notmuchmail.org/mailman/listinfo/notmuch The version of lib/messages.cc in your repo doesn't build because it's missing "#include " (for the uint32_t on line 466). - [ 5-line signature. Click/Enter to show. ] + [ 4-line signature. Click/Enter to show. ] -- Lars Kellogg-Stedman Senior Technologist, Computing and Information Technology Harvard University School of Engineering and Applied Sciences - [ application/pgp-signature ] [ text/plain ] [ 4-line signature. Click/Enter to show. ] @@ -184,7 +183,8 @@ http://notmuchmail.org/mailman/listinfo/notmuch To: notmuch@notmuchmail.org Date: Wed, 18 Nov 2009 02:08:10 -0800 - On Tue, 17 Nov 2009 14:00:54 -0500, Lars Kellogg-Stedman wrote: + On Tue, 17 Nov 2009 14:00:54 -0500, Lars Kellogg-Stedman wrote: > I saw the LWN article and decided to take a look at notmuch. I'm > currently using mutt and mairix to index and read a collection of > Maildir mail folders (around 40,000 messages total). @@ -195,7 +195,7 @@ http://notmuchmail.org/mailman/listinfo/notmuch That's very interesting. So, thanks for coming and trying out notmuch. > Error opening - > /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S: + > /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S: > Too many open files Sadly, the lwn article coincided with me having just introduced this @@ -212,4 +212,3 @@ http://notmuchmail.org/mailman/listinfo/notmuch Happy hacking, -Carl - 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 41e2aaa3..b0bf93ed 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 @@ -26,12 +26,11 @@ with Maildir) or if something else is going on. Cheers, -[ 5-line signature. Click/Enter to show. ] +[ 4-line signature. Click/Enter to show. ] -- Lars Kellogg-Stedman Senior Technologist, Computing and Information Technology Harvard University School of Engineering and Applied Sciences - [ application/pgp-signature ] [ text/plain ] [ 4-line signature. Click/Enter to show. ] @@ -48,12 +47,14 @@ http://notmuchmail.org/mailman/listinfo/notmuch [ multipart/signed ] [ 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: LK> Error opening - LK> /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S: + LK> + /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S: LK> Too many open files See the patch just posted here. @@ -88,12 +89,11 @@ http://notmuchmail.org/mailman/listinfo/notmuch -- Lars - [ 5-line signature. Click/Enter to show. ] + [ 4-line signature. Click/Enter to show. ] -- Lars Kellogg-Stedman Senior Technologist, Computing and Information Technology Harvard University School of Engineering and Applied Sciences - [ application/pgp-signature ] [ text/plain ] [ 4-line signature. Click/Enter to show. ] @@ -106,8 +106,8 @@ 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 @@ -127,14 +127,15 @@ http://notmuchmail.org/mailman/listinfo/notmuch Type: application/pgp-signature Size: 834 bytes Desc: not available - URL: - + URL: + Keith Packard (2009-11-17) (inbox unread) Subject: [notmuch] Working with Maildir storage? To: notmuch@notmuchmail.org Date: Tue, 17 Nov 2009 13:24:13 -0800 - On Tue, 17 Nov 2009 15:33:01 -0500, Lars Kellogg-Stedman wrote: + On Tue, 17 Nov 2009 15:33:01 -0500, Lars Kellogg-Stedman wrote: > > See the patch just posted here. I've also pushed a slightly more complicated (and complete) fix to my @@ -150,7 +151,6 @@ http://notmuchmail.org/mailman/listinfo/notmuch Thanks to everyone for trying out notmuch! -keith - Lars Kellogg-Stedman (2009-11-18) (inbox signed unread) Subject: Re: [notmuch] Working with Maildir storage? To: Keith Packard @@ -166,12 +166,11 @@ http://notmuchmail.org/mailman/listinfo/notmuch The version of lib/messages.cc in your repo doesn't build because it's missing "#include " (for the uint32_t on line 466). - [ 5-line signature. Click/Enter to show. ] + [ 4-line signature. Click/Enter to show. ] -- Lars Kellogg-Stedman Senior Technologist, Computing and Information Technology Harvard University School of Engineering and Applied Sciences - [ application/pgp-signature ] [ text/plain ] [ 4-line signature. Click/Enter to show. ] @@ -184,7 +183,8 @@ http://notmuchmail.org/mailman/listinfo/notmuch To: notmuch@notmuchmail.org Date: Wed, 18 Nov 2009 02:08:10 -0800 - On Tue, 17 Nov 2009 14:00:54 -0500, Lars Kellogg-Stedman wrote: + On Tue, 17 Nov 2009 14:00:54 -0500, Lars Kellogg-Stedman wrote: > I saw the LWN article and decided to take a look at notmuch. I'm > currently using mutt and mairix to index and read a collection of > Maildir mail folders (around 40,000 messages total). @@ -195,7 +195,7 @@ http://notmuchmail.org/mailman/listinfo/notmuch That's very interesting. So, thanks for coming and trying out notmuch. > Error opening - > /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S: + > /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S: > Too many open files Sadly, the lwn article coincided with me having just introduced this @@ -212,4 +212,3 @@ http://notmuchmail.org/mailman/listinfo/notmuch Happy hacking, -Carl - 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 fa2108ef..08de8b5d 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 @@ -26,12 +26,11 @@ with Maildir) or if something else is going on. Cheers, -[ 5-line signature. Click/Enter to show. ] +[ 4-line signature. Click/Enter to show. ] -- Lars Kellogg-Stedman Senior Technologist, Computing and Information Technology Harvard University School of Engineering and Applied Sciences - [ application/pgp-signature ] [ text/plain ] [ 4-line signature. Click/Enter to show. ] @@ -48,12 +47,14 @@ Date: Wed, 18 Nov 2009 01:02:38 +0600 [ multipart/signed ] [ 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: LK> Error opening - LK> /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S: + LK> +/home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S: LK> Too many open files See the patch just posted here. @@ -88,12 +89,11 @@ It doesn't look like the patch is in git yet. -- Lars -[ 5-line signature. Click/Enter to show. ] +[ 4-line signature. Click/Enter to show. ] -- Lars Kellogg-Stedman Senior Technologist, Computing and Information Technology Harvard University School of Engineering and Applied Sciences - [ application/pgp-signature ] [ text/plain ] [ 4-line signature. Click/Enter to show. ] @@ -106,8 +106,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 @@ -127,14 +127,15 @@ Name: not available Type: application/pgp-signature Size: 834 bytes Desc: not available -URL: - +URL: + Keith Packard (2009-11-17) (inbox unread) Subject: [notmuch] Working with Maildir storage? To: notmuch@notmuchmail.org Date: Tue, 17 Nov 2009 13:24:13 -0800 -On Tue, 17 Nov 2009 15:33:01 -0500, Lars Kellogg-Stedman wrote: +On Tue, 17 Nov 2009 15:33:01 -0500, Lars Kellogg-Stedman wrote: > > See the patch just posted here. I've also pushed a slightly more complicated (and complete) fix to my @@ -150,7 +151,6 @@ Barcelona today and so it won't get fixed for a while. Thanks to everyone for trying out notmuch! -keith - Lars Kellogg-Stedman (2009-11-18) (inbox signed unread) Subject: Re: [notmuch] Working with Maildir storage? To: Keith Packard @@ -166,12 +166,11 @@ Date: Tue, 17 Nov 2009 19:50:40 -0500 The version of lib/messages.cc in your repo doesn't build because it's missing "#include " (for the uint32_t on line 466). -[ 5-line signature. Click/Enter to show. ] +[ 4-line signature. Click/Enter to show. ] -- Lars Kellogg-Stedman Senior Technologist, Computing and Information Technology Harvard University School of Engineering and Applied Sciences - [ application/pgp-signature ] [ text/plain ] [ 4-line signature. Click/Enter to show. ] @@ -184,7 +183,8 @@ Subject: [notmuch] Working with Maildir storage? To: notmuch@notmuchmail.org Date: Wed, 18 Nov 2009 02:08:10 -0800 -On Tue, 17 Nov 2009 14:00:54 -0500, Lars Kellogg-Stedman wrote: +On Tue, 17 Nov 2009 14:00:54 -0500, Lars Kellogg-Stedman wrote: > I saw the LWN article and decided to take a look at notmuch. I'm > currently using mutt and mairix to index and read a collection of > Maildir mail folders (around 40,000 messages total). @@ -195,7 +195,7 @@ I hadn't even seen that Keith's blog post had been picked up by lwn.net. That's very interesting. So, thanks for coming and trying out notmuch. > Error opening -> /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S: +> /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S: > Too many open files Sadly, the lwn article coincided with me having just introduced this @@ -212,4 +212,3 @@ likes the best. Happy hacking, -Carl - diff --git a/test/encoding b/test/encoding index 673b0394..e875c8b1 100755 --- a/test/encoding +++ b/test/encoding @@ -12,7 +12,7 @@ Notmuch Test Suite (2001-01-05) (inbox unread) Subject: Test message #1 From: Notmuch Test Suite To: Notmuch Test Suite -Date: Tue, 05 Jan 2001 15:43:57 -0000 +Date: Fri, 05 Jan 2001 15:43:57 +0000 header} body{ part{ ID: 1, Content-type: text/plain diff --git a/test/maildir-sync b/test/maildir-sync index a60854f8..d5872a53 100755 --- a/test/maildir-sync +++ b/test/maildir-sync @@ -55,8 +55,8 @@ test_expect_equal "$output" '[[[{"id": "adding-replied-tag@notmuch-test-suite", "To": "Notmuch Test Suite ", "Cc": "", "Bcc": "", -"Date": "Tue, -05 Jan 2001 15:43:57 -0000"}, +"Date": "Fri, +05 Jan 2001 15:43:57 +0000"}, "body": [{"id": 1, "content-type": "text/plain", "content": "This is just a test message (#3)\n"}]}, diff --git a/test/notmuch-test b/test/notmuch-test index ded79e8f..e40ef86a 100755 --- a/test/notmuch-test +++ b/test/notmuch-test @@ -42,6 +42,7 @@ TESTS=" encoding emacs emacs-large-search-buffer + emacs-subject-to-filename maildir-sync crypto symbol-hiding diff --git a/test/python b/test/python index c3aa7266..6018c2d0 100755 --- a/test/python +++ b/test/python @@ -7,11 +7,25 @@ add_email_corpus test_begin_subtest "compare thread ids" test_python < EXPECTED -test_expect_equal_file <(sort OUTPUT) EXPECTED +notmuch search --sort=oldest-first --output=threads tag:inbox | sed s/^thread:// > EXPECTED +test_expect_equal_file OUTPUT EXPECTED + +test_begin_subtest "compare message ids" +test_python < EXPECTED +test_expect_equal_file OUTPUT EXPECTED + test_done diff --git a/test/raw b/test/raw index 99d3a3bf..0171e641 100755 --- a/test/raw +++ b/test/raw @@ -19,7 +19,7 @@ test_expect_equal "$output" "From: Notmuch Test Suite Message-Id: Subject: Test message #1 -Date: Tue, 05 Jan 2001 15:43:57 -0000 +Date: Fri, 05 Jan 2001 15:43:57 +0000 This is just a test message (#1)" @@ -29,7 +29,7 @@ test_expect_equal "$output" "From: Notmuch Test Suite Message-Id: Subject: Test message #2 -Date: Tue, 05 Jan 2001 15:43:57 -0000 +Date: Fri, 05 Jan 2001 15:43:57 +0000 This is just a test message (#2)" diff --git a/test/search b/test/search index e7c8c54b..a7a0b18d 100755 --- a/test/search +++ b/test/search @@ -79,8 +79,11 @@ output=$(notmuch search 'subject:"subject search test (phrase)"' | notmuch_searc test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; subject search test (phrase) (inbox unread)" test_begin_subtest 'Search for all messages ("*")' -output=$(notmuch search '*' | notmuch_search_sanitize) -test_expect_equal "$output" "thread:XXX 2009-11-18 [1/1] Chris Wilson; [notmuch] [PATCH 1/2] Makefile: evaluate pkg-config once (inbox unread) +notmuch search '*' | notmuch_search_sanitize > OUTPUT +cat <EXPECTED +thread:XXX 2010-12-29 [1/1] François Boulogne; [aur-general] Guidelines: cp, mkdir vs install (inbox unread) +thread:XXX 2010-12-16 [1/1] Olivier Berger; Essai accentué (inbox unread) +thread:XXX 2009-11-18 [1/1] Chris Wilson; [notmuch] [PATCH 1/2] Makefile: evaluate pkg-config once (inbox unread) thread:XXX 2009-11-18 [2/2] Alex Botero-Lowry, Carl Worth; [notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop (attachment inbox unread) thread:XXX 2009-11-18 [2/2] Ingmar Vanhassel, Carl Worth; [notmuch] [PATCH] Typsos (inbox unread) thread:XXX 2009-11-18 [3/3] Adrian Perez de Castro, Keith Packard, Carl Worth; [notmuch] Introducing myself (inbox signed unread) @@ -99,7 +102,7 @@ thread:XXX 2009-11-18 [1/1] Jan Janak; [notmuch] [PATCH] notmuch new: Support thread:XXX 2009-11-18 [1/1] Stewart Smith; [notmuch] [PATCH] count_files: sort directory in inode order before statting (inbox unread) thread:XXX 2009-11-18 [1/1] Stewart Smith; [notmuch] [PATCH 2/2] Read mail directory in inode number order (inbox unread) thread:XXX 2009-11-18 [1/1] Stewart Smith; [notmuch] [PATCH] Fix linking with gcc to use g++ to link in C++ libs. (inbox unread) -thread:XXX 2009-11-18 [2/2] Lars Kellogg-Stedman; [notmuch] \"notmuch help\" outputs to stderr? (attachment inbox signed unread) +thread:XXX 2009-11-18 [2/2] Lars Kellogg-Stedman; [notmuch] "notmuch help" outputs to stderr? (attachment inbox signed unread) thread:XXX 2009-11-17 [1/1] Mikhail Gusarov; [notmuch] [PATCH] Handle rename of message file (inbox unread) thread:XXX 2009-11-17 [2/2] Alex Botero-Lowry, Carl Worth; [notmuch] preliminary FreeBSD support (attachment inbox unread) thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; body search (inbox unread) @@ -117,7 +120,9 @@ thread:XXX 2000-01-01 [1/1] Search By From Name; search by from (name) (inbox thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by to (address) (inbox unread) thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by to (name) (inbox unread) thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; subject search test (phrase) (inbox unread) -thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; this phrase should not match the subject search test (inbox unread)" +thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; this phrase should not match the subject search test (inbox unread) +EOF +test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "Search body (utf-8):" add_message '[subject]="utf8-message-body-subject"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[body]="message body utf8: bödý"' diff --git a/test/search-output b/test/search-output index 10291c3b..8b57a432 100755 --- a/test/search-output +++ b/test/search-output @@ -29,6 +29,8 @@ thread:THREADID thread:THREADID thread:THREADID thread:THREADID +thread:THREADID +thread:THREADID EOF test_expect_equal_file OUTPUT EXPECTED @@ -56,6 +58,8 @@ cat <EXPECTED "THREADID", "THREADID", "THREADID", +"THREADID", +"THREADID", "THREADID"] EOF test_expect_equal_file OUTPUT EXPECTED @@ -63,6 +67,8 @@ test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "--output=messages" notmuch search --output=messages '*' >OUTPUT cat <EXPECTED +id:4EFC743A.3060609@april.org +id:877h1wv7mg.fsf@inf-8657.int-evry.fr id:1258544095-16616-1-git-send-email-chris@chris-wilson.co.uk id:877htoqdbo.fsf@yoom.home.cworth.org id:878we4qdqf.fsf@yoom.home.cworth.org @@ -119,7 +125,9 @@ test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "--output=messages --format=json" notmuch search --format=json --output=messages '*' >OUTPUT cat <EXPECTED -["1258544095-16616-1-git-send-email-chris@chris-wilson.co.uk", +["4EFC743A.3060609@april.org", +"877h1wv7mg.fsf@inf-8657.int-evry.fr", +"1258544095-16616-1-git-send-email-chris@chris-wilson.co.uk", "877htoqdbo.fsf@yoom.home.cworth.org", "878we4qdqf.fsf@yoom.home.cworth.org", "87aaykqe24.fsf@yoom.home.cworth.org", @@ -175,6 +183,8 @@ test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "--output=files" notmuch search --output=files '*' | sed -e "s,$MAIL_DIR,MAIL_DIR," >OUTPUT cat <EXPECTED +MAIL_DIR/cur/52:2, +MAIL_DIR/cur/53:2, MAIL_DIR/cur/50:2, MAIL_DIR/cur/49:2, MAIL_DIR/cur/48:2, @@ -232,7 +242,9 @@ test_expect_equal_file OUTPUT EXPECTED test_begin_subtest "--output=files --format=json" notmuch search --format=json --output=files '*' | sed -e "s,$MAIL_DIR,MAIL_DIR," >OUTPUT cat <EXPECTED -["MAIL_DIR/cur/50:2,", +["MAIL_DIR/cur/52:2,", +"MAIL_DIR/cur/53:2,", +"MAIL_DIR/cur/50:2,", "MAIL_DIR/cur/49:2,", "MAIL_DIR/cur/48:2,", "MAIL_DIR/cur/47:2,", diff --git a/test/symbol-hiding b/test/symbol-hiding index 68f0d1b1..7fa7b2aa 100755 --- a/test/symbol-hiding +++ b/test/symbol-hiding @@ -12,7 +12,7 @@ test_description='exception symbol hiding' . ./test-lib.sh run_test(){ - result=$(LD_LIBRARY_PATH=$TEST_DIRECTORY/../lib $TEST_DIRECTORY/symbol-test 2>&1) + result=$(LD_LIBRARY_PATH="$TEST_DIRECTORY/../lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" $TEST_DIRECTORY/symbol-test 2>&1) } output="A Xapian exception occurred opening database: Couldn't stat 'fakedb/.notmuch/xapian' diff --git a/test/test-lib.sh b/test/test-lib.sh index b5e346c0..82767c07 100644 --- a/test/test-lib.sh +++ b/test/test-lib.sh @@ -1,4 +1,3 @@ -#!/usr/bin/env bash # # Copyright (c) 2005 Junio C Hamano # @@ -50,6 +49,7 @@ TZ=UTC TERM=dumb export LANG LC_ALL PAGER TERM TZ GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u} +TEST_EMACS=${TEST_EMACS:-${EMACS:-emacs}} # Protect ourselves from common misconfiguration to export # CDPATH into the environment @@ -322,7 +322,7 @@ generate_message () fi if [ -z "${template[date]}" ]; then - template[date]="Tue, 05 Jan 2001 15:43:57 -0000" + template[date]="Fri, 05 Jan 2001 15:43:57 +0000" fi additional_headers="" @@ -897,7 +897,7 @@ export NOTMUCH_CONFIG=$NOTMUCH_CONFIG # # --load Force loading of notmuch.el and test-lib.el -exec emacs --no-init-file --no-site-file \ +exec ${TEST_EMACS} --no-init-file --no-site-file \ --directory "$TEST_DIRECTORY/../emacs" --load notmuch.el \ --directory "$TEST_DIRECTORY" --load test-lib.el \ "\$@"