mirror of
https://git.notmuchmail.org/git/notmuch
synced 2025-01-03 15:21:41 +01:00
notmuch Debian 0.22-1 upload (same as 0.22)
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQGcBAABCAAGBQJXIA7bAAoJEPIClx2kp54sZSYL/3uJRbUP2rSe/S7PfEh+iQJY /4/mbkS1hz72dDH8A111zB4b05aJ5LeWcMRWklY+6ScATjXSmT3xBNggZbFPmL77 wfVBcIldwrU7SgoEIW3SyoLPQETlj1gCOTQ0gpEkSFzDw+s4ZBFu9DHf/OfQvJ7I elAMdCDzZrgX8UJU4wjmSifLj43rIfAJ1yfCzyvUKeEsJ0eJmUYP5Ia4siSULJrU /RqZotdCMbN4/viR0JeOV6SVowPJF4nIP1jTIux150cubnm1XMEr3rs1Dx/xrywR 8iYy068hhtUpsFQ0+JcVWfmxUlMnJg9SxFSjJAqYwSCl23ukoBybBbjzEII//eYh tIVWs2z/VvoOuTgCTuMJp2imz9rhfX/yG/DdY5orupU5AgWklo42i69CoF1pEyhY PwCl+OfIv2RfGd6/QE90J8qGEbmy4lv/1sWpbfUGzIZ+y9ePcu/CrWlvhoRKW0LV W1nHUvpP15cFV/g5zjwi4iKocduSxvhhD9GW4AzLvA== =8VzP -----END PGP SIGNATURE----- Merge tag 'debian/0.22-1' into jessie-backports notmuch Debian 0.22-1 upload (same as 0.22)
This commit is contained in:
commit
7fe91ddc4f
102 changed files with 2524 additions and 669 deletions
135
NEWS
135
NEWS
|
@ -1,3 +1,136 @@
|
|||
Notmuch 0.22 (2016-04-26)
|
||||
=========================
|
||||
|
||||
General
|
||||
-------
|
||||
|
||||
Xapian 1.3 support
|
||||
|
||||
Notmuch should now build (and the test suite should pass) on recent
|
||||
releases of Xapian 1.3.x. It has been tested with Xapian 1.3.5.
|
||||
|
||||
Limited support for S/MIME messages
|
||||
|
||||
Signature verification is supported, but not decryption. S/MIME
|
||||
signature creation and S/MIME encryption are supported via built-in
|
||||
support in Emacs. S/MIME support is not extensively tested at this
|
||||
time.
|
||||
|
||||
Bug Fixes
|
||||
|
||||
Fix for threading bug involving deleting and re-adding
|
||||
messages. Fix for case-sensitive content disposition headers. Fix
|
||||
handling of 1 character directory names at top level.
|
||||
|
||||
Command Line Interface
|
||||
----------------------
|
||||
|
||||
`notmuch show` now supports verifying S/MIME signatures
|
||||
|
||||
This support relies on an appropriately configured `gpgsm`.
|
||||
|
||||
Build System
|
||||
------------
|
||||
|
||||
Drop dependency on "pkg-config emacs".
|
||||
|
||||
Emacs Interface
|
||||
---------------
|
||||
|
||||
Notmuch replies now include all parts shown in the show view
|
||||
|
||||
There are two main user visible changes. The first is that rfc822
|
||||
parts are now included in replies.
|
||||
|
||||
The second change is that part headers are now included in the reply
|
||||
buffer to provide visible separation of the parts. The choice of
|
||||
which part headers to show is customizable via the variable
|
||||
`notmuch-mua-reply-insert-header-p-function`.
|
||||
|
||||
Filtering or Limiting messages is now bound to `l` in the search view
|
||||
|
||||
This binding now matches the analogous binding in show view.
|
||||
|
||||
`F` forwards all open messages in a thread
|
||||
|
||||
When viewing a thread of messages, the new binding `F` can be used
|
||||
to generate a new outgoing message which forwards all of the open
|
||||
messages in the thread. This is analogous to the `f` binding, which
|
||||
forwards only the current message.
|
||||
|
||||
Preferred content type can be determined from the message content
|
||||
|
||||
More flexibility in choosing which sub-part of a
|
||||
multipart/alternative part is initially shown is available by
|
||||
setting `notmuch-multipart/alternative-discouraged` to a function
|
||||
that returns a list of discouraged types. The function so specified
|
||||
is passed the message as an argument and can examine the message
|
||||
content to determine which content types should be discouraged. This
|
||||
is in addition to the current capabilities (i.e. setting
|
||||
`notmuch-multipart/alternative-discouraged` to a list of discouraged
|
||||
types).
|
||||
|
||||
When viewing a thread ("show" mode), queries that match no messages no
|
||||
longer generate empty buffers
|
||||
|
||||
Should an attempt be made to view the thread corresponding to a
|
||||
query that matches no messages, a warning message is now displayed
|
||||
and the terminal bell rung rather than displaying an empty buffer
|
||||
(or, in some cases, displaying an empty buffer and throwing an
|
||||
error). This also affects re-display of the current thread.
|
||||
|
||||
Handle S/MIME signatures in emacs
|
||||
|
||||
The emacs interface is now capable making and verifying S/MIME
|
||||
signatures.
|
||||
|
||||
`notmuch-message-address-insinuate` is now a no-op
|
||||
|
||||
This reduces the amount of interference with non-notmuch uses of
|
||||
message-mode.
|
||||
|
||||
Address completion improvements
|
||||
|
||||
An external script is no longer needed for address completion; if
|
||||
you previously configured one, customize the variable
|
||||
`notmuch-address-command` to try the internal completion. If
|
||||
`company-mode` is available, notmuch uses it by default for
|
||||
interactive address completion.
|
||||
|
||||
Test and experiment with the emacs MUA available in source tree
|
||||
|
||||
`./devel/try-emacs-mua` runs emacs and fills the window with
|
||||
information how to try the MUA safely. Emacs is configured to use
|
||||
the notmuch (lisp) files located in `./emacs` directory.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
New `notmuch-report(1)` and `notmuch-report.json(5)` man pages
|
||||
describe `notmuch-report` and its JSON configuration file. You can
|
||||
build these files by running `make` in the `devel/nmbug/doc`
|
||||
directory.
|
||||
|
||||
notmuch-report
|
||||
--------------
|
||||
|
||||
Renamed from `nmbug-status`. This script generates reports based on
|
||||
notmuch queries, and doesn't really have anything to do with nmbug,
|
||||
except for sharing the `NMBGIT` environment variable. The new name
|
||||
focuses on the script's action, instead of its historical association
|
||||
with the nmbug workflow. This should make it more discoverable for
|
||||
users looking for generic notmuch reporting tools.
|
||||
|
||||
The default configuration file name (extracted from the `config`
|
||||
branch of `NBMGIT` has changed from `status-config.json` to
|
||||
`notmuch-report.json` so it is more obviously associated with the
|
||||
report-generating script. The configuration file also has a new
|
||||
`meta.message-url` setting, which is documented in
|
||||
`notmuch-report.json(5)`.
|
||||
|
||||
`notmuch-report` now wraps query phrases in parentheses when and-ing
|
||||
them together, to avoid confusion about clause grouping.
|
||||
|
||||
Notmuch 0.21 (2015-10-29)
|
||||
=========================
|
||||
|
||||
|
@ -409,7 +542,7 @@ from the config file. Use something like:
|
|||
...
|
||||
},
|
||||
...
|
||||
},
|
||||
}
|
||||
|
||||
Python Bindings
|
||||
---------------
|
||||
|
|
|
@ -10,8 +10,6 @@ ifeq ($(HAVE_RUBY_DEV),1)
|
|||
LIBNOTMUCH="../../lib/$(LINKER_NAME)" \
|
||||
ruby extconf.rb --vendor
|
||||
$(MAKE) -C $(dir)/ruby
|
||||
else
|
||||
@echo Missing dependency, skipping ruby bindings
|
||||
endif
|
||||
|
||||
CLEAN += $(patsubst %,$(dir)/ruby/%, \
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
notmuch -- The python interface to notmuch.so
|
||||
==============================================
|
||||
notmuch -- The python interface to notmuch
|
||||
==========================================
|
||||
|
||||
This module makes the functionality of the notmuch library
|
||||
(`http://notmuchmail.org`_) available to python. Successful import of
|
||||
|
@ -10,78 +10,8 @@ If you have downloaded the full source tarball, you can create the
|
|||
documentation with sphinx installed, go to the docs directory and
|
||||
"make html". A static version of the documentation is available at:
|
||||
|
||||
http://packages.python.org/notmuch/
|
||||
https://notmuch.readthedocs.org/projects/notmuch-python/
|
||||
|
||||
The current source code is being hosted at
|
||||
http://bitbucket.org/spaetz/cnotmuch which also provides an issue
|
||||
tracker, and release downloads. This package is tracked by the python
|
||||
package index repository at `http://pypi.python.org/pypi/notmuch`_ and can thus be installed on a user's computer easily via "sudo easy_install notmuch" (you will still need to install the notmuch shared library separately as it is not included in this package).
|
||||
To build the python bindings, do
|
||||
|
||||
The original source has been provided by (c)Sebastian Spaeth, 2010.
|
||||
All code is available under the GNU GPLv3+ (see docs/COPYING) unless specified otherwise.
|
||||
|
||||
|
||||
INSTALLATION & DEINSTALL
|
||||
------------------------
|
||||
|
||||
The notmuch python module is available on pypi.python.org. This means
|
||||
you can do "easy_install notmuch" on your linux box and it will get
|
||||
installed into:
|
||||
|
||||
/usr/local/lib/python2.x/dist-packages/
|
||||
|
||||
For uninstalling, you'll need to remove the "notmuch-0.4-py2.x.egg"
|
||||
(or similar) directory and delete one entry in the "easy-install.pth"
|
||||
file in that directory.
|
||||
|
||||
It needs to have a libnotmuch.so or libnotmuch.so.1 available in some
|
||||
library folder or will raise an exception when loading.
|
||||
"OSError: libnotmuch.so.1: cannot open shared object file: No such file or directory"
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
For more examples of how to use the notmuch interface, have a look at the
|
||||
notmuch "binary" and the generated documentation.
|
||||
|
||||
Example session:
|
||||
>>>import notmuch
|
||||
>>>db = notmuch.Database("/home/spaetz/mail")
|
||||
db.get_path()
|
||||
'/home/spaetz/mail'
|
||||
>>>tags = db.get_all_tags()
|
||||
>>>for tag in tags:
|
||||
>>> print tag
|
||||
inbox
|
||||
...
|
||||
maildir::draft
|
||||
#---------------------------------------------
|
||||
|
||||
q = notmuch.Query(db,'from:Sebastian')
|
||||
count = len(q.search_messages())
|
||||
1300
|
||||
|
||||
#---------------------------------------------
|
||||
|
||||
>>>db = notmuch.Database("/home/spaetz/mailHAHA")
|
||||
NotmuchError: Could not open the specified database
|
||||
|
||||
#---------------------------------------------
|
||||
|
||||
>>>tags = notmuch.Database("/home/spaetz/mail").get_all_tags()
|
||||
>>>del(tags)
|
||||
|
||||
|
||||
Building for a Debian package
|
||||
------------------------------
|
||||
dpkg-buildpackage -i"\.hg|\/build"
|
||||
|
||||
|
||||
Changelog
|
||||
---------
|
||||
0.1 First public release
|
||||
0.1.1 Fixed Database.create_query()
|
||||
0.2.0 Implemented Thread() and Threads() methods
|
||||
0.2.1 Implemented the remaining API methods, notably Directory() and Filenames()
|
||||
0.2.2 Bug fixes
|
||||
0.3.0 Incorporated in the notmuchmail.org git repository
|
||||
python setup.py install --prefix=path/to/your/preferred/location
|
||||
|
|
|
@ -8,7 +8,11 @@ Files and directories
|
|||
|
||||
.. autoclass:: Filenames
|
||||
|
||||
.. automethod:: Filenames.__len__
|
||||
.. method:: Filenames.__len__
|
||||
.. warning::
|
||||
:meth:`__len__` was removed in version 0.22 as it exhausted the
|
||||
iterator and broke list(Filenames()). Use `len(list(names))`
|
||||
instead.
|
||||
|
||||
:class:`Directoy` -- A directory entry in the database
|
||||
------------------------------------------------------
|
||||
|
|
|
@ -47,5 +47,11 @@ The following exceptions are all directly derived from NotmuchError. Each of the
|
|||
:members:
|
||||
.. autoexception:: UnbalancedAtomicError(message=None)
|
||||
:members:
|
||||
.. autoexception:: UnsupportedOperationError(message=None)
|
||||
:members:
|
||||
.. autoexception:: UpgradeRequiredError(message=None)
|
||||
:members:
|
||||
.. autoexception:: PathError(message=None)
|
||||
:members:
|
||||
.. autoexception:: NotInitializedError(message=None)
|
||||
:members:
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
|
||||
.. autoclass:: Threads
|
||||
|
||||
.. automethod:: __len__
|
||||
.. method:: __len__
|
||||
.. warning::
|
||||
:meth:`__len__` was removed in version 0.22 as it exhausted the
|
||||
iterator and broke list(Threads()). Use `len(list(msgs))`
|
||||
instead.
|
||||
|
||||
.. automethod:: __str__
|
||||
.. automethod:: __str__
|
||||
|
|
|
@ -75,6 +75,9 @@ from .errors import (
|
|||
UnbalancedFreezeThawError,
|
||||
UnbalancedAtomicError,
|
||||
NotInitializedError,
|
||||
UnsupportedOperationError,
|
||||
UpgradeRequiredError,
|
||||
PathError,
|
||||
)
|
||||
from .version import __VERSION__
|
||||
__LICENSE__ = "GPL v3+"
|
||||
|
|
|
@ -65,3 +65,7 @@ else:
|
|||
raise TypeError('Expected str, got %s' % type(value))
|
||||
|
||||
return value.encode('utf-8', 'replace')
|
||||
|
||||
# We import the SafeConfigParser class on behalf of other code to cope
|
||||
# with the differences between Python 2 and 3.
|
||||
SafeConfigParser # avoid warning about unused import
|
||||
|
|
|
@ -36,7 +36,6 @@ from .errors import (
|
|||
NotmuchError,
|
||||
NullPointerError,
|
||||
NotInitializedError,
|
||||
ReadOnlyDatabaseError,
|
||||
)
|
||||
from .message import Message
|
||||
from .tag import Tags
|
||||
|
@ -484,7 +483,10 @@ class Database(object):
|
|||
removed.
|
||||
"""
|
||||
self._assert_db_is_initialized()
|
||||
return self._remove_message(self._db, _str(filename))
|
||||
status = self._remove_message(self._db, _str(filename))
|
||||
if status not in [STATUS.SUCCESS, STATUS.DUPLICATE_MESSAGE_ID]:
|
||||
raise NotmuchError(status)
|
||||
return status
|
||||
|
||||
def find_message(self, msgid):
|
||||
"""Returns a :class:`Message` as identified by its message ID
|
||||
|
@ -575,6 +577,22 @@ class Database(object):
|
|||
"""
|
||||
return Query(self, querystring)
|
||||
|
||||
"""notmuch_database_status_string"""
|
||||
_status_string = nmlib.notmuch_database_status_string
|
||||
_status_string.argtypes = [NotmuchDatabaseP]
|
||||
_status_string.restype = c_char_p
|
||||
|
||||
def status_string(self):
|
||||
"""Returns the status string of the database
|
||||
|
||||
This is sometimes used for additional error reporting
|
||||
"""
|
||||
self._assert_db_is_initialized()
|
||||
s = Database._status_string(self._db)
|
||||
if s:
|
||||
return s.decode('utf-8', 'ignore')
|
||||
return s
|
||||
|
||||
def __repr__(self):
|
||||
return "'Notmuch DB " + self.get_path() + "'"
|
||||
|
||||
|
|
|
@ -56,6 +56,9 @@ STATUS = Status(['SUCCESS',
|
|||
'TAG_TOO_LONG',
|
||||
'UNBALANCED_FREEZE_THAW',
|
||||
'UNBALANCED_ATOMIC',
|
||||
'UNSUPPORTED_OPERATION',
|
||||
'UPGRADE_REQUIRED',
|
||||
'PATH_ERROR',
|
||||
'NOT_INITIALIZED'])
|
||||
"""STATUS is a class, whose attributes provide constants that serve as return
|
||||
indicators for notmuch functions. Currently the following ones are defined. For
|
||||
|
@ -73,6 +76,9 @@ description.
|
|||
* TAG_TOO_LONG
|
||||
* UNBALANCED_FREEZE_THAW
|
||||
* UNBALANCED_ATOMIC
|
||||
* UNSUPPORTED_OPERATION
|
||||
* UPGRADE_REQUIRED
|
||||
* PATH_ERROR
|
||||
* NOT_INITIALIZED
|
||||
|
||||
Invoke the class method `notmuch.STATUS.status2str` with a status value as
|
||||
|
@ -101,6 +107,9 @@ class NotmuchError(Exception, Python3StringMixIn):
|
|||
STATUS.TAG_TOO_LONG: TagTooLongError,
|
||||
STATUS.UNBALANCED_FREEZE_THAW: UnbalancedFreezeThawError,
|
||||
STATUS.UNBALANCED_ATOMIC: UnbalancedAtomicError,
|
||||
STATUS.UNSUPPORTED_OPERATION: UnsupportedOperationError,
|
||||
STATUS.UPGRADE_REQUIRED: UpgradeRequiredError,
|
||||
STATUS.PATH_ERROR: PathError,
|
||||
STATUS.NOT_INITIALIZED: NotInitializedError,
|
||||
}
|
||||
assert 0 < status <= len(subclasses)
|
||||
|
@ -175,6 +184,18 @@ class UnbalancedAtomicError(NotmuchError):
|
|||
status = STATUS.UNBALANCED_ATOMIC
|
||||
|
||||
|
||||
class UnsupportedOperationError(NotmuchError):
|
||||
status = STATUS.UNSUPPORTED_OPERATION
|
||||
|
||||
|
||||
class UpgradeRequiredError(NotmuchError):
|
||||
status = STATUS.UPGRADE_REQUIRED
|
||||
|
||||
|
||||
class PathError(NotmuchError):
|
||||
status = STATUS.PATH_ERROR
|
||||
|
||||
|
||||
class NotInitializedError(NotmuchError):
|
||||
"""Derived from NotmuchError, this occurs if the underlying data
|
||||
structure (e.g. database is not initialized (yet) or an iterator has
|
||||
|
|
|
@ -19,7 +19,6 @@ Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>
|
|||
from ctypes import c_char_p
|
||||
from .globals import (
|
||||
nmlib,
|
||||
NotmuchMessageP,
|
||||
NotmuchFilenamesP,
|
||||
Python3StringMixIn,
|
||||
)
|
||||
|
@ -48,7 +47,7 @@ class Filenames(Python3StringMixIn):
|
|||
|
||||
as well as::
|
||||
|
||||
number_of_names = len(names)
|
||||
list_of_names = list(names)
|
||||
|
||||
and even a simple::
|
||||
|
||||
|
@ -123,28 +122,10 @@ class Filenames(Python3StringMixIn):
|
|||
return "\n".join(self)
|
||||
|
||||
_destroy = nmlib.notmuch_filenames_destroy
|
||||
_destroy.argtypes = [NotmuchMessageP]
|
||||
_destroy.argtypes = [NotmuchFilenamesP]
|
||||
_destroy.restype = None
|
||||
|
||||
def __del__(self):
|
||||
"""Close and free the notmuch filenames"""
|
||||
if self._files_p:
|
||||
self._destroy(self._files_p)
|
||||
|
||||
def __len__(self):
|
||||
"""len(:class:`Filenames`) returns the number of contained files
|
||||
|
||||
.. note::
|
||||
|
||||
This method exhausts the iterator object, so you will not be able to
|
||||
iterate over them again.
|
||||
"""
|
||||
if not self._files_p:
|
||||
raise NotInitializedError()
|
||||
|
||||
i = 0
|
||||
while self._valid(self._files_p):
|
||||
self._move_to_next(self._files_p)
|
||||
i += 1
|
||||
self._files_p = None
|
||||
return i
|
||||
|
|
|
@ -33,6 +33,11 @@ except:
|
|||
|
||||
from .compat import Python3StringMixIn, encode_utf8 as _str
|
||||
|
||||
# We import these on behalf of other modules. Silence warning about
|
||||
# these symbols not being used.
|
||||
Python3StringMixIn
|
||||
_str
|
||||
|
||||
class Enum(object):
|
||||
"""Provides ENUMS as "code=Enum(['a','b','c'])" where code.a=0 etc..."""
|
||||
def __init__(self, names):
|
||||
|
|
|
@ -28,6 +28,7 @@ from .globals import (
|
|||
NotmuchMessagesP,
|
||||
)
|
||||
from .errors import (
|
||||
NotmuchError,
|
||||
NullPointerError,
|
||||
NotInitializedError,
|
||||
)
|
||||
|
@ -133,10 +134,10 @@ class Query(object):
|
|||
self._assert_query_is_initialized()
|
||||
self._exclude_tag(self._query, _str(tagname))
|
||||
|
||||
"""notmuch_query_search_threads"""
|
||||
_search_threads = nmlib.notmuch_query_search_threads
|
||||
_search_threads.argtypes = [NotmuchQueryP]
|
||||
_search_threads.restype = NotmuchThreadsP
|
||||
"""notmuch_query_search_threads_st"""
|
||||
_search_threads_st = nmlib.notmuch_query_search_threads_st
|
||||
_search_threads_st.argtypes = [NotmuchQueryP, POINTER(NotmuchThreadsP)]
|
||||
_search_threads_st.restype = c_uint
|
||||
|
||||
def search_threads(self):
|
||||
"""Execute a query for threads
|
||||
|
@ -153,16 +154,19 @@ class Query(object):
|
|||
:raises: :exc:`NullPointerError` if search_threads failed
|
||||
"""
|
||||
self._assert_query_is_initialized()
|
||||
threads_p = Query._search_threads(self._query)
|
||||
threads_p = NotmuchThreadsP() # == NULL
|
||||
status = Query._search_threads_st(self._query, byref(threads_p))
|
||||
if status != 0:
|
||||
raise NotmuchError(status)
|
||||
|
||||
if not threads_p:
|
||||
raise NullPointerError
|
||||
return Threads(threads_p, self)
|
||||
|
||||
"""notmuch_query_search_messages"""
|
||||
_search_messages = nmlib.notmuch_query_search_messages
|
||||
_search_messages.argtypes = [NotmuchQueryP]
|
||||
_search_messages.restype = NotmuchMessagesP
|
||||
"""notmuch_query_search_messages_st"""
|
||||
_search_messages_st = nmlib.notmuch_query_search_messages_st
|
||||
_search_messages_st.argtypes = [NotmuchQueryP, POINTER(NotmuchMessagesP)]
|
||||
_search_messages_st.restype = c_uint
|
||||
|
||||
def search_messages(self):
|
||||
"""Filter messages according to the query and return
|
||||
|
@ -172,7 +176,10 @@ class Query(object):
|
|||
:raises: :exc:`NullPointerError` if search_messages failed
|
||||
"""
|
||||
self._assert_query_is_initialized()
|
||||
msgs_p = Query._search_messages(self._query)
|
||||
msgs_p = NotmuchMessagesP() # == NULL
|
||||
status = Query._search_messages_st(self._query, byref(msgs_p))
|
||||
if status != 0:
|
||||
raise NotmuchError(status)
|
||||
|
||||
if not msgs_p:
|
||||
raise NullPointerError
|
||||
|
|
|
@ -46,7 +46,7 @@ class Threads(Python3StringMixIn):
|
|||
|
||||
as well as::
|
||||
|
||||
number_of_msgs = len(threads)
|
||||
list_of_threads = list(threads)
|
||||
|
||||
will "exhaust" the threads. If you need to re-iterate over a list of
|
||||
messages you will need to retrieve a new :class:`Threads` object.
|
||||
|
@ -64,8 +64,7 @@ class Threads(Python3StringMixIn):
|
|||
for thread in threads:
|
||||
threadlist.append(thread)
|
||||
|
||||
# threads is "exhausted" now and even len(threads) will raise an
|
||||
# exception.
|
||||
# threads is "exhausted" now.
|
||||
# However it will be kept around until all retrieved Thread() objects are
|
||||
# also deleted. If you did e.g. an explicit del(threads) here, the
|
||||
# following lines would fail.
|
||||
|
@ -132,30 +131,6 @@ class Threads(Python3StringMixIn):
|
|||
return thread
|
||||
next = __next__ # python2.x iterator protocol compatibility
|
||||
|
||||
def __len__(self):
|
||||
"""len(:class:`Threads`) returns the number of contained Threads
|
||||
|
||||
.. note:: As this iterates over the threads, we will not be able to
|
||||
iterate over them again! So this will fail::
|
||||
|
||||
#THIS FAILS
|
||||
threads = Database().create_query('').search_threads()
|
||||
if len(threads) > 0: #this 'exhausts' threads
|
||||
# next line raises :exc:`NotInitializedError`!!!
|
||||
for thread in threads: print thread
|
||||
"""
|
||||
if not self._threads:
|
||||
raise NotInitializedError()
|
||||
|
||||
i = 0
|
||||
# returns 'bool'. On out-of-memory it returns None
|
||||
while self._valid(self._threads):
|
||||
self._move_to_next(self._threads)
|
||||
i += 1
|
||||
# reset self._threads to mark as "exhausted"
|
||||
self._threads = None
|
||||
return i
|
||||
|
||||
def __nonzero__(self):
|
||||
'''
|
||||
Implement truth value testing. If __nonzero__ is not
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
# this file should be kept in sync with ../../../version
|
||||
__VERSION__ = '0.21'
|
||||
__VERSION__ = '0.22'
|
||||
SOVERSION = '4'
|
||||
|
|
48
configure
vendored
48
configure
vendored
|
@ -51,7 +51,7 @@ CPPFLAGS=${CPPFLAGS:-}
|
|||
CXXFLAGS_for_sh=${CXXFLAGS:-${CFLAGS}}
|
||||
CXXFLAGS=${CXXFLAGS:-\$(CFLAGS)}
|
||||
LDFLAGS=${LDFLAGS:-}
|
||||
XAPIAN_CONFIG=${XAPIAN_CONFIG:-xapian-config}
|
||||
XAPIAN_CONFIG=${XAPIAN_CONFIG:-}
|
||||
PYTHON=${PYTHON:-}
|
||||
|
||||
# We don't allow the EMACS or GZIP Makefile variables inherit values
|
||||
|
@ -341,7 +341,7 @@ fi
|
|||
|
||||
printf "Checking for Xapian development files... "
|
||||
have_xapian=0
|
||||
for xapian_config in ${XAPIAN_CONFIG}; do
|
||||
for xapian_config in ${XAPIAN_CONFIG} xapian-config xapian-config-1.3; do
|
||||
if ${xapian_config} --version > /dev/null 2>&1; then
|
||||
xapian_version=$(${xapian_config} --version | sed -e 's/.* //')
|
||||
printf "Yes (%s).\n" ${xapian_version}
|
||||
|
@ -371,7 +371,25 @@ if [ ${have_xapian} = "1" ]; then
|
|||
esac
|
||||
fi
|
||||
|
||||
|
||||
default_xapian_backend=""
|
||||
if [ ${have_xapian} = "1" ]; then
|
||||
printf "Testing default Xapian backend... "
|
||||
cat >_default_backend.cc <<EOF
|
||||
#include <xapian.h>
|
||||
int main(int argc, char** argv) {
|
||||
Xapian::WritableDatabase db("test.db",Xapian::DB_CREATE_OR_OPEN);
|
||||
}
|
||||
EOF
|
||||
${CXX} ${CXXLAGS} ${xapian_cxxflags} _default_backend.cc -o _default_backend ${xapian_ldflags}
|
||||
./_default_backend
|
||||
if [ -f test.db/iamglass ]; then
|
||||
default_xapian_backend=glass
|
||||
else
|
||||
default_xapian_backend=chert
|
||||
fi
|
||||
printf "${default_xapian_backend}\n";
|
||||
rm -rf test.db _default_backend _default_backend.cc
|
||||
fi
|
||||
# we need to have a version >= 2.6.5 to avoid a crypto bug. We need
|
||||
# 2.6.7 for permissive "From " header handling.
|
||||
GMIME_MINVER=2.6.7
|
||||
|
@ -472,19 +490,11 @@ else
|
|||
fi
|
||||
|
||||
if [ -z "${EMACSLISPDIR}" ]; then
|
||||
if pkg-config --exists emacs; then
|
||||
EMACSLISPDIR=$(pkg-config emacs --variable sitepkglispdir)
|
||||
else
|
||||
EMACSLISPDIR='$(prefix)/share/emacs/site-lisp'
|
||||
fi
|
||||
EMACSLISPDIR='$(prefix)/share/emacs/site-lisp'
|
||||
fi
|
||||
|
||||
if [ -z "${EMACSETCDIR}" ]; then
|
||||
if pkg-config --exists emacs; then
|
||||
EMACSETCDIR=$(pkg-config emacs --variable sitepkglispdir)
|
||||
else
|
||||
EMACSETCDIR='$(prefix)/share/emacs/site-lisp'
|
||||
fi
|
||||
EMACSETCDIR='$(prefix)/share/emacs/site-lisp'
|
||||
fi
|
||||
|
||||
printf "Checking if emacs is available... "
|
||||
|
@ -977,6 +987,10 @@ HAVE_STRCASESTR = ${have_strcasestr}
|
|||
# build its own version)
|
||||
HAVE_STRSEP = ${have_strsep}
|
||||
|
||||
# Whether the timegm function is available (if not, then notmuch will
|
||||
# build its own version)
|
||||
HAVE_TIMEGM = ${have_timegm}
|
||||
|
||||
# Whether struct dirent has d_type (if not, then notmuch will use stat)
|
||||
HAVE_D_TYPE = ${have_d_type}
|
||||
|
||||
|
@ -1005,6 +1019,9 @@ LINKER_RESOLVES_LIBRARY_DEPENDENCIES = ${linker_resolves_library_dependencies}
|
|||
XAPIAN_CXXFLAGS = ${xapian_cxxflags}
|
||||
XAPIAN_LDFLAGS = ${xapian_ldflags}
|
||||
|
||||
# Which backend will Xapian use by default?
|
||||
DEFAULT_XAPIAN_BACKEND = ${default_xapian_backend}
|
||||
|
||||
# Flags needed to compile and link against GMime
|
||||
GMIME_CFLAGS = ${gmime_cflags}
|
||||
GMIME_LDFLAGS = ${gmime_ldflags}
|
||||
|
@ -1049,6 +1066,7 @@ CONFIGURE_CFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS) \\
|
|||
\$(VALGRIND_CFLAGS) \\
|
||||
-DHAVE_STRCASESTR=\$(HAVE_STRCASESTR) \\
|
||||
-DHAVE_STRSEP=\$(HAVE_STRSEP) \\
|
||||
-DHAVE_TIMEGM=\$(HAVE_TIMEGM) \\
|
||||
-DHAVE_D_TYPE=\$(HAVE_D_TYPE) \\
|
||||
-DSTD_GETPWUID=\$(STD_GETPWUID) \\
|
||||
-DSTD_ASCTIME=\$(STD_ASCTIME) \\
|
||||
|
@ -1062,6 +1080,7 @@ CONFIGURE_CXXFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS) \\
|
|||
\$(VALGRIND_CFLAGS) \$(XAPIAN_CXXFLAGS) \\
|
||||
-DHAVE_STRCASESTR=\$(HAVE_STRCASESTR) \\
|
||||
-DHAVE_STRSEP=\$(HAVE_STRSEP) \\
|
||||
-DHAVE_TIMEGM=\$(HAVE_TIMEGM) \\
|
||||
-DHAVE_D_TYPE=\$(HAVE_D_TYPE) \\
|
||||
-DSTD_GETPWUID=\$(STD_GETPWUID) \\
|
||||
-DSTD_ASCTIME=\$(STD_ASCTIME) \\
|
||||
|
@ -1079,6 +1098,9 @@ cat > sh.config <<EOF
|
|||
# Whether the Xapian version in use supports compaction
|
||||
NOTMUCH_HAVE_XAPIAN_COMPACT=${have_xapian_compact}
|
||||
|
||||
# Which backend will Xapian use by default?
|
||||
NOTMUCH_DEFAULT_XAPIAN_BACKEND=${default_xapian_backend}
|
||||
|
||||
# do we have man pages?
|
||||
NOTMUCH_HAVE_MAN=$((have_sphinx))
|
||||
|
||||
|
|
80
crypto.c
80
crypto.c
|
@ -22,14 +22,20 @@
|
|||
|
||||
/* Create a GPG context (GMime 2.6) */
|
||||
static notmuch_crypto_context_t *
|
||||
create_gpg_context (const char *gpgpath)
|
||||
create_gpg_context (notmuch_crypto_t *crypto)
|
||||
{
|
||||
notmuch_crypto_context_t *gpgctx;
|
||||
|
||||
if (crypto->gpgctx)
|
||||
return crypto->gpgctx;
|
||||
|
||||
/* TODO: GMimePasswordRequestFunc */
|
||||
gpgctx = g_mime_gpg_context_new (NULL, gpgpath ? gpgpath : "gpg");
|
||||
if (! gpgctx)
|
||||
gpgctx = g_mime_gpg_context_new (NULL, crypto->gpgpath ? crypto->gpgpath : "gpg");
|
||||
if (! gpgctx) {
|
||||
fprintf (stderr, "Failed to construct gpg context.\n");
|
||||
return NULL;
|
||||
}
|
||||
crypto->gpgctx = gpgctx;
|
||||
|
||||
g_mime_gpg_context_set_use_agent ((GMimeGpgContext *) gpgctx, TRUE);
|
||||
g_mime_gpg_context_set_always_trust ((GMimeGpgContext *) gpgctx, FALSE);
|
||||
|
@ -37,12 +43,57 @@ create_gpg_context (const char *gpgpath)
|
|||
return gpgctx;
|
||||
}
|
||||
|
||||
/* Create a PKCS7 context (GMime 2.6) */
|
||||
static notmuch_crypto_context_t *
|
||||
create_pkcs7_context (notmuch_crypto_t *crypto)
|
||||
{
|
||||
notmuch_crypto_context_t *pkcs7ctx;
|
||||
|
||||
if (crypto->pkcs7ctx)
|
||||
return crypto->pkcs7ctx;
|
||||
|
||||
/* TODO: GMimePasswordRequestFunc */
|
||||
pkcs7ctx = g_mime_pkcs7_context_new (NULL);
|
||||
if (! pkcs7ctx) {
|
||||
fprintf (stderr, "Failed to construct pkcs7 context.\n");
|
||||
return NULL;
|
||||
}
|
||||
crypto->pkcs7ctx = pkcs7ctx;
|
||||
|
||||
g_mime_pkcs7_context_set_always_trust ((GMimePkcs7Context *) pkcs7ctx,
|
||||
FALSE);
|
||||
|
||||
return pkcs7ctx;
|
||||
}
|
||||
static const struct {
|
||||
const char *protocol;
|
||||
notmuch_crypto_context_t *(*get_context) (notmuch_crypto_t *crypto);
|
||||
} protocols[] = {
|
||||
{
|
||||
.protocol = "application/pgp-signature",
|
||||
.get_context = create_gpg_context,
|
||||
},
|
||||
{
|
||||
.protocol = "application/pgp-encrypted",
|
||||
.get_context = create_gpg_context,
|
||||
},
|
||||
{
|
||||
.protocol = "application/pkcs7-signature",
|
||||
.get_context = create_pkcs7_context,
|
||||
},
|
||||
{
|
||||
.protocol = "application/x-pkcs7-signature",
|
||||
.get_context = create_pkcs7_context,
|
||||
},
|
||||
};
|
||||
|
||||
/* for the specified protocol return the context pointer (initializing
|
||||
* if needed) */
|
||||
notmuch_crypto_context_t *
|
||||
notmuch_crypto_get_context (notmuch_crypto_t *crypto, const char *protocol)
|
||||
{
|
||||
notmuch_crypto_context_t *cryptoctx = NULL;
|
||||
size_t i;
|
||||
|
||||
if (! protocol) {
|
||||
fprintf (stderr, "Cryptographic protocol is empty.\n");
|
||||
|
@ -55,19 +106,15 @@ notmuch_crypto_get_context (notmuch_crypto_t *crypto, const char *protocol)
|
|||
* parameter names as defined in this document are
|
||||
* case-insensitive." Thus, we use strcasecmp for the protocol.
|
||||
*/
|
||||
if (strcasecmp (protocol, "application/pgp-signature") == 0 ||
|
||||
strcasecmp (protocol, "application/pgp-encrypted") == 0) {
|
||||
if (! crypto->gpgctx) {
|
||||
crypto->gpgctx = create_gpg_context (crypto->gpgpath);
|
||||
if (! crypto->gpgctx)
|
||||
fprintf (stderr, "Failed to construct gpg context.\n");
|
||||
}
|
||||
cryptoctx = crypto->gpgctx;
|
||||
} else {
|
||||
fprintf (stderr, "Unknown or unsupported cryptographic protocol.\n");
|
||||
for (i = 0; i < ARRAY_SIZE (protocols); i++) {
|
||||
if (strcasecmp (protocol, protocols[i].protocol) == 0)
|
||||
return protocols[i].get_context (crypto);
|
||||
}
|
||||
|
||||
return cryptoctx;
|
||||
fprintf (stderr, "Unknown or unsupported cryptographic protocol %s.\n",
|
||||
protocol);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -78,5 +125,10 @@ notmuch_crypto_cleanup (notmuch_crypto_t *crypto)
|
|||
crypto->gpgctx = NULL;
|
||||
}
|
||||
|
||||
if (crypto->pkcs7ctx) {
|
||||
g_object_unref (crypto->pkcs7ctx);
|
||||
crypto->pkcs7ctx = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
19
debian/changelog
vendored
19
debian/changelog
vendored
|
@ -1,3 +1,22 @@
|
|||
notmuch (0.22-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release. See /usr/share/doc/notmuch/NEWS for new
|
||||
features and bug fixes.
|
||||
|
||||
-- David Bremner <bremner@debian.org> Tue, 26 Apr 2016 21:31:44 -0300
|
||||
|
||||
notmuch (0.22~rc1-1) experimental; urgency=medium
|
||||
|
||||
* Upstream release candidate
|
||||
|
||||
-- David Bremner <bremner@debian.org> Sun, 24 Apr 2016 18:03:15 -0300
|
||||
|
||||
notmuch (0.22~rc0-1) experimental; urgency=medium
|
||||
|
||||
* Upstream release candidate
|
||||
|
||||
-- David Bremner <bremner@debian.org> Sat, 16 Apr 2016 08:45:32 -0300
|
||||
|
||||
notmuch (0.21-3~bpo8+1) jessie-backports; urgency=medium
|
||||
|
||||
* Rebuild for jessie-backports.
|
||||
|
|
4
debian/control
vendored
4
debian/control
vendored
|
@ -7,6 +7,7 @@ Uploaders:
|
|||
David Bremner <bremner@debian.org>
|
||||
Build-Conflicts: ruby1.8, gdb-minimal, gdb [s390x ia64 armel ppc64el mips mipsel mips64el]
|
||||
Build-Depends:
|
||||
dpkg-dev (>= 1.17.14),
|
||||
debhelper (>= 9),
|
||||
pkg-config,
|
||||
libxapian-dev,
|
||||
|
@ -22,6 +23,7 @@ Build-Depends:
|
|||
emacs23-nox | emacs23 (>=23~) | emacs23-lucid (>=23~),
|
||||
gdb [!s390x !ia64 !armel !ppc64el !mips !mipsel !mips64el],
|
||||
dtach (>= 0.8),
|
||||
gpgsm <!nocheck>,
|
||||
bash-completion (>=1.9.0~)
|
||||
Standards-Version: 3.9.6
|
||||
Homepage: http://notmuchmail.org/
|
||||
|
@ -31,7 +33,7 @@ Vcs-Browser: http://git.notmuchmail.org/git/notmuch
|
|||
Package: notmuch
|
||||
Architecture: any
|
||||
Depends: libnotmuch4 (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends}
|
||||
Recommends: notmuch-emacs | notmuch-vim | notmuch-mutt | alot, gnupg-agent
|
||||
Recommends: notmuch-emacs | notmuch-vim | notmuch-mutt | alot, gnupg-agent, gpgsm
|
||||
Description: thread-based email index, search and tagging
|
||||
Notmuch is a system for indexing, searching, reading, and tagging
|
||||
large collections of email messages in maildir or mh format. It uses
|
||||
|
|
11
devel/STYLE
11
devel/STYLE
|
@ -25,9 +25,7 @@ The following nonsense code demonstrates many aspects of the style:
|
|||
static some_type
|
||||
function (param_type param, param_type param)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
int j;
|
||||
|
||||
j = i + 10;
|
||||
|
@ -64,12 +62,19 @@ function (param_type param, param_type param)
|
|||
* Code lines should be less than 80 columns and comments should be
|
||||
wrapped at 70 columns.
|
||||
|
||||
* Variable declarations should be at the top of a block; C99 style
|
||||
control variable declarations in for loops are also OK.
|
||||
|
||||
Naming
|
||||
------
|
||||
|
||||
* Use lowercase_with_underscores for function, variable, and type
|
||||
names.
|
||||
|
||||
* Except for variables with extremely small scope, and perhaps loop
|
||||
indices, when naming variables and functions, err on the side of
|
||||
verbosity.
|
||||
|
||||
* All structs should be typedef'd to a name ending with _t. If the
|
||||
struct has a tag, it should be the same as the typedef name, minus
|
||||
the trailing _t.
|
||||
|
|
2
devel/nmbug/doc/.gitignore
vendored
Normal file
2
devel/nmbug/doc/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
*.pyc
|
||||
_build
|
38
devel/nmbug/doc/Makefile
Normal file
38
devel/nmbug/doc/Makefile
Normal file
|
@ -0,0 +1,38 @@
|
|||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
DOCBUILDDIR := _build
|
||||
|
||||
SRCDIR ?= .
|
||||
ALLSPHINXOPTS := -d $(DOCBUILDDIR)/doctrees $(SPHINXOPTS) $(SRCDIR)
|
||||
|
||||
MAN_RST_FILES := $(shell find $(SRCDIR)/man* -name '*.rst')
|
||||
MAN_ROFF_FILES := $(patsubst $(SRCDIR)/man%.rst,$(DOCBUILDDIR)/man/man%,$(MAN_RST_FILES))
|
||||
MAN_GZIP_FILES := $(addsuffix .gz,$(MAN_ROFF_FILES))
|
||||
|
||||
.PHONY: build-man
|
||||
build-man: $(MAN_GZIP_FILES)
|
||||
|
||||
%.gz: %
|
||||
rm -f $@ && gzip --stdout $^ > $@
|
||||
|
||||
$(MAN_ROFF_FILES): $(DOCBUILDDIR)/.roff.stamp
|
||||
|
||||
# By using $(DOCBUILDDIR)/.roff.stamp instead of $(MAN_ROFF_FILES), we
|
||||
# convey to make that a single invocation of this recipe builds all
|
||||
# of the roff files. This prevents parallel make from starting an
|
||||
# instance of this recipe for each roff file.
|
||||
$(DOCBUILDDIR)/.roff.stamp $(MAN_ROFF_FILES): $(MAN_RST_FILES)
|
||||
mkdir -p $(DOCBUILDDIR)
|
||||
touch $(DOCBUILDDIR)/.roff.stamp
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(DOCBUILDDIR)/man
|
||||
for section in 1 5; do \
|
||||
mkdir -p $(DOCBUILDDIR)/man/man$${section}; \
|
||||
mv $(DOCBUILDDIR)/man/*.$${section} $(DOCBUILDDIR)/man/man$${section}; \
|
||||
done
|
||||
|
||||
clean:
|
||||
rm -rf $(DOCBUILDDIR) $(SRCDIR)/conf.pyc
|
67
devel/nmbug/doc/conf.py
Normal file
67
devel/nmbug/doc/conf.py
Normal file
|
@ -0,0 +1,67 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os.path
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'notmuch'
|
||||
authors = 'Carl Worth and many others'
|
||||
copyright = '2009-2015, {0}'.format(authors)
|
||||
|
||||
location = os.path.dirname(__file__)
|
||||
|
||||
dirname = location
|
||||
while True:
|
||||
version_file = os.path.join(dirname, 'version')
|
||||
if os.path.exists(version_file):
|
||||
with open(version_file,'r') as f:
|
||||
version = f.read().strip()
|
||||
break
|
||||
if dirname == '/':
|
||||
raise ValueError(
|
||||
'no version file found in this directory or its ancestors')
|
||||
dirname = os.path.dirname(dirname)
|
||||
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
|
||||
man_pages = [
|
||||
('man1/notmuch-report.1', 'notmuch-report',
|
||||
'generate reports from notmuch queries', [authors], 1),
|
||||
('man5/notmuch-report.json.5', 'notmuch-report.json',
|
||||
'configure notmuch-report', [authors], 5),
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#man_show_urls = False
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
texinfo_no_detailmenu = True
|
||||
|
||||
texinfo_documents = [
|
||||
('man1/notmuch-report.1', 'notmuch-report',
|
||||
'generate reports from notmuch queries', authors, 'notmuch-report',
|
||||
'generate reports from notmuch queries', 'Miscellaneous'),
|
||||
('man5/notmuch-report.json.5', 'notmuch-report.json',
|
||||
'configure notmuch-report', authors, 'notmuch-report.json',
|
||||
'configure notmuch-report', 'Miscellaneous'),
|
||||
]
|
17
devel/nmbug/doc/index.rst
Normal file
17
devel/nmbug/doc/index.rst
Normal file
|
@ -0,0 +1,17 @@
|
|||
Welcome to notmuch's dev-tool documentation!
|
||||
============================================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:titlesonly:
|
||||
|
||||
man1/notmuch-report.1
|
||||
man5/notmuch-report.json.5
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
54
devel/nmbug/doc/man1/notmuch-report.1.rst
Normal file
54
devel/nmbug/doc/man1/notmuch-report.1.rst
Normal file
|
@ -0,0 +1,54 @@
|
|||
==============
|
||||
notmuch-report
|
||||
==============
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**notmuch-report** [options ...]
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
|
||||
Generate HTML or plain-text reports showing query results.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
|
||||
``-h``, ``--help``
|
||||
|
||||
Show a help message, including a list of available options, and
|
||||
exit.
|
||||
|
||||
``--text``
|
||||
Output plain text instead of HTML.
|
||||
|
||||
``--config`` <PATH>
|
||||
Load config from given file. The format is described in
|
||||
**notmuch-report.json(5)**. If this option is not set,
|
||||
**notmuch-report** loads the config from the Git repository at
|
||||
``NMBGIT``. See :ref:`NMBGIT <NMBGIT>` for details.
|
||||
|
||||
``--list-views``
|
||||
List available views (by title) and exit.
|
||||
|
||||
``--get-query`` <VIEW>
|
||||
Print the configured query for view matching the given title.
|
||||
|
||||
ENVIRONMENT
|
||||
===========
|
||||
|
||||
.. _NMBGIT:
|
||||
|
||||
``NMBGIT``
|
||||
If ``--config PATH`` is not set, **notmuch-report** will attempt
|
||||
to load a config file named ``notmuch-report.json`` from the
|
||||
``config`` branch of the ``NMBGIT`` repository (defaulting to
|
||||
``~/.nmbug``).
|
||||
|
||||
SEE ALSO
|
||||
========
|
||||
|
||||
**notmuch(1)**, **notmuch-report.json(5)**, **notmuch-search(1)**,
|
||||
**notmuch-tag(1)**
|
||||
|
129
devel/nmbug/doc/man5/notmuch-report.json.5.rst
Normal file
129
devel/nmbug/doc/man5/notmuch-report.json.5.rst
Normal file
|
@ -0,0 +1,129 @@
|
|||
==============
|
||||
notmuch-report
|
||||
==============
|
||||
|
||||
NAME
|
||||
====
|
||||
|
||||
notmuch-report.json - configure output for **notmuch-report(1)**
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
|
||||
The config file is JSON_ with the following fields:
|
||||
|
||||
meta
|
||||
An object with page-wide information
|
||||
|
||||
title
|
||||
Page title used in the default header.
|
||||
|
||||
blurb
|
||||
Introduction paragraph used in the default header.
|
||||
|
||||
header
|
||||
`Python format string`_ for the HTML header. Optional. It is
|
||||
formatted with the following context:
|
||||
|
||||
date
|
||||
The current UTC date.
|
||||
|
||||
datetime
|
||||
The current UTC date-time.
|
||||
|
||||
title
|
||||
The **meta.title** value.
|
||||
|
||||
blurb
|
||||
The **meta.blurb** value.
|
||||
|
||||
encoding
|
||||
The encoding used for the output file.
|
||||
|
||||
inter_message_padding
|
||||
0.25em, for consistent CSS generation.
|
||||
|
||||
border_radius
|
||||
0.5em, for consistent CSS generation.
|
||||
|
||||
footer
|
||||
`Python format string`_ for the HTML footer. It is formatted with
|
||||
the same context used for **meta.header**. Optional.
|
||||
|
||||
message-url
|
||||
`Python format string`_ for message-linking URLs. Optional.
|
||||
Defaults to linking Gmane_. It is formatted with the following
|
||||
context:
|
||||
|
||||
message-id
|
||||
The quoted_ message ID.
|
||||
|
||||
subject
|
||||
The message subject.
|
||||
|
||||
views
|
||||
An array of view objects, where each object has the following
|
||||
fields:
|
||||
|
||||
title
|
||||
Header text for the view.
|
||||
|
||||
comment
|
||||
Paragraph describing the view in more detail. Optional.
|
||||
|
||||
id
|
||||
Anchor string for the view. Optional, defaulting to a slugged
|
||||
form of the view title
|
||||
|
||||
query
|
||||
An array of strings, which will be joined with 'and' to form the
|
||||
view query.
|
||||
|
||||
.. _Gmane: http://gmane.org/
|
||||
.. _JSON: http://json.org/
|
||||
.. _Python format string: https://docs.python.org/3/library/string.html#formatstrings
|
||||
.. _quoted: https://docs.python.org/3/library/urllib.parse.html#urllib.parse.quote
|
||||
|
||||
EXAMPLE
|
||||
=======
|
||||
|
||||
::
|
||||
|
||||
{
|
||||
"meta": {
|
||||
"title": "Notmuch Patches",
|
||||
"blurb": "For more information see <a href=\"http://notmuchmail.org/nmbug\">nmbug</a>",
|
||||
"header": "<html><head></head><body><h1>{title}</h1><p>{blurb}</p><h2>Views</h2>",
|
||||
"footer": "<hr><p>Generated: {datetime}</p></html>",
|
||||
"message-url": "http://mid.gmane.org/{message-id}"
|
||||
},
|
||||
"views": [
|
||||
{
|
||||
"title": "Bugs",
|
||||
"comment": "Unresolved bugs.",
|
||||
"query": [
|
||||
"tag:notmuch::bug",
|
||||
"not tag:notmuch::fixed",
|
||||
"not tag:notmuch::wontfix"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Review",
|
||||
"comment": "These patches are under review, or waiting for feedback.",
|
||||
"id": "under-review",
|
||||
"query": [
|
||||
"tag:notmuch::patch",
|
||||
"not tag:notmuch::pushed",
|
||||
"not tag:notmuch::obsolete",
|
||||
"not tag:notmuch::stale",
|
||||
"not tag:notmuch::wontfix",
|
||||
"(tag:notmuch::moreinfo or tag:notmuch::needs-review)"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
SEE ALSO
|
||||
========
|
||||
|
||||
**notmuch(1)**, **notmuch-report(1)**, **notmuch-search(1)**, **notmuch-tag(1)**
|
|
@ -608,6 +608,8 @@ def _index_tags():
|
|||
stdin=_subprocess.PIPE,
|
||||
additional_env={'GIT_INDEX_FILE': path}) as git:
|
||||
for line in notmuch.stdout:
|
||||
if line.strip().startswith('#'):
|
||||
continue
|
||||
(tags_string, id) = [_.strip() for _ in line.split(' -- id:')]
|
||||
tags = [
|
||||
_unquote(tag[len(prefix):])
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see http://www.gnu.org/licenses/ .
|
||||
|
||||
"""Generate HTML for one or more notmuch searches.
|
||||
"""Generate text and/or HTML for one or more notmuch searches.
|
||||
|
||||
Messages matching each search are grouped by thread. Each message
|
||||
that contains both a subject and message-id will have the displayed
|
||||
subject link to the Gmane view of the message.
|
||||
subject link to an archive view of the message (defaulting to Gmane).
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
@ -88,7 +88,7 @@ def read_config(path=None, encoding=None):
|
|||
else:
|
||||
nmbhome = os.getenv('NMBGIT', os.path.expanduser('~/.nmbug'))
|
||||
branch = 'config'
|
||||
filename = 'status-config.json'
|
||||
filename = 'notmuch-report.json'
|
||||
|
||||
# read only the first line from the pipe
|
||||
sha1_bytes = subprocess.Popen(
|
||||
|
@ -109,9 +109,9 @@ def read_config(path=None, encoding=None):
|
|||
status = p.wait()
|
||||
if status != 0:
|
||||
raise ConfigError(
|
||||
("Missing status-config.json in branch '{branch}' of"
|
||||
'{nmbgit}. Add the file or explicitly set --config.'
|
||||
).format(branch=branch, nmbgit=nmbhome))
|
||||
("Missing {filename} in branch '{branch}' of {nmbgit}. "
|
||||
'Add the file or explicitly set --config.'
|
||||
).format(filename=filename, branch=branch, nmbgit=nmbhome))
|
||||
|
||||
config_json = config_bytes.decode(encoding)
|
||||
try:
|
||||
|
@ -167,7 +167,8 @@ class Page (object):
|
|||
view['title'], sort_key))
|
||||
if 'query-string' not in view:
|
||||
query = view['query']
|
||||
view['query-string'] = ' and '.join(query)
|
||||
view['query-string'] = ' and '.join(
|
||||
'( {} )'.format(q) for q in query)
|
||||
q = notmuch.Query(database, view['query-string'])
|
||||
q.set_sort(sort)
|
||||
threads = self._get_threads(messages=q.search_messages())
|
||||
|
@ -232,6 +233,10 @@ class Page (object):
|
|||
class HtmlPage (Page):
|
||||
_slug_regexp = re.compile('\W+')
|
||||
|
||||
def __init__(self, message_url_template, **kwargs):
|
||||
self.message_url_template = message_url_template
|
||||
super(HtmlPage, self).__init__(**kwargs)
|
||||
|
||||
def _write_header(self, views, stream):
|
||||
super(HtmlPage, self)._write_header(views=views, stream=stream)
|
||||
stream.write('<ul>\n')
|
||||
|
@ -292,8 +297,9 @@ class HtmlPage (Page):
|
|||
'message-id': quote(display_data['message-id']),
|
||||
'subject': xml.sax.saxutils.escape(display_data['subject']),
|
||||
}
|
||||
d['url'] = self.message_url_template.format(**d)
|
||||
display_data['subject'] = (
|
||||
'<a href="http://mid.gmane.org/{message-id}">{subject}</a>'
|
||||
'<a href="{url}">{subject}</a>'
|
||||
).format(**d)
|
||||
for key in ['message-id', 'from']:
|
||||
if key in display_data:
|
||||
|
@ -304,14 +310,17 @@ class HtmlPage (Page):
|
|||
return self._slug_regexp.sub('-', string)
|
||||
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--text', help='output plain text format',
|
||||
action='store_true')
|
||||
parser.add_argument('--config', help='load config from given file',
|
||||
metavar='PATH')
|
||||
parser.add_argument('--list-views', help='list views',
|
||||
action='store_true')
|
||||
parser.add_argument('--get-query', help='get query for view',
|
||||
metavar='VIEW')
|
||||
parser.add_argument(
|
||||
'--text', action='store_true', help='output plain text format')
|
||||
parser.add_argument(
|
||||
'--config', metavar='PATH',
|
||||
help='load config from given file. '
|
||||
'The format is described in notmuch-report.json(5).')
|
||||
parser.add_argument(
|
||||
'--list-views', action='store_true', help='list views')
|
||||
parser.add_argument(
|
||||
'--get-query', metavar='VIEW', help='get query for view')
|
||||
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
@ -327,6 +336,15 @@ header_template = config['meta'].get('header', '''<!DOCTYPE html>
|
|||
<meta http-equiv="Content-Type" content="text/html; charset={encoding}" />
|
||||
<title>{title}</title>
|
||||
<style media="screen" type="text/css">
|
||||
h1 {{
|
||||
font-size: 1.5em;
|
||||
}}
|
||||
h2 {{
|
||||
font-size: 1.17em;
|
||||
}}
|
||||
h3 {{
|
||||
font-size: 100%;
|
||||
}}
|
||||
table {{
|
||||
border-spacing: 0;
|
||||
}}
|
||||
|
@ -367,15 +385,16 @@ header_template = config['meta'].get('header', '''<!DOCTYPE html>
|
|||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>{title}</h2>
|
||||
<h1>{title}</h1>
|
||||
<p>
|
||||
{blurb}
|
||||
</p>
|
||||
<h3>Views</h3>
|
||||
<h2>Views</h2>
|
||||
''')
|
||||
|
||||
footer_template = config['meta'].get('footer', '''
|
||||
<hr>
|
||||
<p>Generated: {datetime}
|
||||
<p>Generated: {datetime}</p>
|
||||
</body>
|
||||
</html>
|
||||
''')
|
||||
|
@ -395,6 +414,8 @@ _PAGES['text'] = Page()
|
|||
_PAGES['html'] = HtmlPage(
|
||||
header=header_template.format(**context),
|
||||
footer=footer_template.format(**context),
|
||||
message_url_template=config['meta'].get(
|
||||
'message-url', 'http://mid.gmane.org/{message-id}'),
|
||||
)
|
||||
|
||||
if args.list_views:
|
||||
|
@ -404,7 +425,7 @@ if args.list_views:
|
|||
elif args.get_query != None:
|
||||
for view in config['views']:
|
||||
if args.get_query == view['title']:
|
||||
print(' and '.join(view['query']))
|
||||
print(' and '.join('( {} )'.format(q) for q in view['query']))
|
||||
sys.exit(0)
|
||||
else:
|
||||
# only import notmuch if needed
|
|
@ -62,7 +62,7 @@
|
|||
"not tag:notmuch::obsolete",
|
||||
"not tag:notmuch::stale",
|
||||
"not tag:notmuch::wontfix",
|
||||
"(tag:notmuch::moreinfo or tag:notmuch::needs-review)"
|
||||
"tag:notmuch::moreinfo or tag:notmuch::needs-review"
|
||||
],
|
||||
"title": "Review"
|
||||
}
|
|
@ -175,6 +175,21 @@ case $news_date in
|
|||
append_emsg "Date '$news_date' in NEWS file is not in format (yyyy-mm-dd)"
|
||||
esac
|
||||
|
||||
year=`exec date +%Y`
|
||||
echo -n "Checking that copyright in documentation contains 2009-$year... "
|
||||
# Read the value of variable `copyright' defined in 'doc/conf.py'.
|
||||
# As __file__ is not defined when python command is given from command line,
|
||||
# it is defined before contents of 'doc/conf.py' (which dereferences __file__)
|
||||
# is executed.
|
||||
copyrightline=`exec python -c "with open('doc/conf.py') as cf: __file__ = ''; exec(cf.read()); print(copyright)"`
|
||||
case $copyrightline in
|
||||
*2009-$year*)
|
||||
echo Yes. ;;
|
||||
*)
|
||||
echo No.
|
||||
append_emsg "The copyright in doc/conf.py line '$copyrightline' does not contain '2009-$year'"
|
||||
esac
|
||||
|
||||
if [ -n "$emsgs" ]
|
||||
then
|
||||
echo
|
||||
|
|
157
devel/try-emacs-mua
Executable file
157
devel/try-emacs-mua
Executable file
|
@ -0,0 +1,157 @@
|
|||
#!/bin/sh
|
||||
:; set -x; exec "${EMACS:-emacs}" --debug-init --load "$0" "$@"; exit
|
||||
;;
|
||||
;; Try the notmuch emacs client located in ../emacs/ directory
|
||||
;;
|
||||
;; Run this without arguments; emacs window opens with some usage information
|
||||
;;
|
||||
;; Authors: Tomi Ollila <tomi.ollila@iki.fi>
|
||||
;;
|
||||
;; http://www.emacswiki.org/emacs/EmacsScripts was a useful starting point...
|
||||
;;
|
||||
;; Licence: GPLv3+
|
||||
;;
|
||||
|
||||
(message "Starting '%s'" load-file-name)
|
||||
|
||||
(set-buffer "*scratch*")
|
||||
|
||||
(setq initial-buffer-choice nil
|
||||
inhibit-startup-screen t)
|
||||
|
||||
(when (featurep 'notmuch)
|
||||
(insert "
|
||||
Notmuch has been loaded to this emacs (during processing of the init file)
|
||||
which means it is (most probably) loaded from different source than expected.
|
||||
|
||||
Please run \"" (file-name-nondirectory load-file-name)
|
||||
"\" with '-q' (or '-Q') as an argument, to disable
|
||||
processing of the init file -- you can load it after emacs has started\n
|
||||
exit emacs (y or n)? ")
|
||||
(if (y-or-n-p "exit emacs")
|
||||
(kill-emacs)
|
||||
(error "Stopped reading %s" load-file-name)))
|
||||
|
||||
(let ((pdir (file-name-directory
|
||||
(directory-file-name (file-name-directory load-file-name)))))
|
||||
(unless (file-exists-p (concat pdir "emacs/notmuch-lib.el"))
|
||||
(insert "Cannot find notmuch-emacs source directory
|
||||
while looking at: " pdir "emacs\n\nexit emacs (y or n)? ")
|
||||
(if (y-or-n-p "exit emacs")
|
||||
(kill-emacs)
|
||||
(error "Stopped reading %s" load-file-name)))
|
||||
(setq try-notmuch-source-directory (directory-file-name pdir)
|
||||
try-notmuch-emacs-directory (concat pdir "emacs/")
|
||||
load-path (cons try-notmuch-emacs-directory load-path)))
|
||||
|
||||
;; they say advice doesn't work for primitives (functions from c source)
|
||||
;; well, these 'before' advice works for emacs 23.1 - 24.5 (at least)
|
||||
;; ...and for our purposes 24.3 is enough (there is no load-prefer-newer there)
|
||||
;; note also that the old, "obsolete" defadvice mechanism was used, but that
|
||||
;; is the only one available for emacs 23 and 24 up to 24.3.
|
||||
|
||||
(if (boundp 'load-prefer-newer)
|
||||
(defadvice require (before before-require activate)
|
||||
(unless (featurep feature)
|
||||
(message "require: %s" feature)))
|
||||
;; else: special require "short-circuit"; after load feature is provided...
|
||||
;; ... in notmuch sources we always use require and there are no loops
|
||||
(defadvice require (before before-require activate)
|
||||
(unless (featurep feature)
|
||||
(message "require: %s" feature)
|
||||
(let ((name (symbol-name feature)))
|
||||
(if (and (string-match "^notmuch" name)
|
||||
(file-newer-than-file-p
|
||||
(concat try-notmuch-emacs-directory name ".el")
|
||||
(concat try-notmuch-emacs-directory name ".elc")))
|
||||
(load (concat try-notmuch-emacs-directory name ".el") nil nil t t)
|
||||
)))))
|
||||
|
||||
(insert "Found notmuch emacs client in " try-notmuch-emacs-directory "\n")
|
||||
|
||||
(let ((notmuch-path (executable-find "notmuch")))
|
||||
(insert "Notmuch CLI executable "
|
||||
(if notmuch-path (concat "is " notmuch-path) "not found!") "\n"))
|
||||
|
||||
(condition-case err
|
||||
;; "opportunistic" load-prefer-newer -- will be effective since emacs 24.4
|
||||
(let ((load-prefer-newer t)
|
||||
(force-load-messages t))
|
||||
(require 'notmuch))
|
||||
;; specifying `debug' here lets the debugger run
|
||||
;; if `debug-on-error' is non-nil.
|
||||
((debug error)
|
||||
(let ((error-message-string (error-message-string err)))
|
||||
(insert "\nLoading notmuch failed: " error-message-string "\n")
|
||||
(message "Loading notmuch failed: %s" error-message-string)
|
||||
(insert "See *Messages* buffer for more information.\n")
|
||||
(if init-file-user
|
||||
(message "Hint: %s -q (or -Q) may help" load-file-name))
|
||||
(pop-to-buffer "*Messages*")
|
||||
(error "Stopped reading %s" load-file-name))))
|
||||
|
||||
(insert "
|
||||
Go to the end of the following lines and type C-x C-e to evaluate
|
||||
(or C-j which is shorter but inserts evaluation results into buffer)
|
||||
|
||||
To \"disable\" mail sending, evaluate
|
||||
* (setq message-send-mail-function (lambda () t))
|
||||
")
|
||||
|
||||
(if (file-exists-p (concat try-notmuch-source-directory "/notmuch"))
|
||||
(insert "
|
||||
To use accompanied notmuch binary from the same source, evaluate
|
||||
* (setq exec-path (cons \"" try-notmuch-source-directory "\" exec-path))
|
||||
Note: Evaluating the above may be followed by unintended database
|
||||
upgrade and getting back to old version may require dump & restore.
|
||||
"))
|
||||
|
||||
(if init-file-user ;; nil, if '-q' or '-Q' is given, but no '-u' 'USER'
|
||||
(insert "
|
||||
Your init file was processed during emacs startup. If you want to test
|
||||
notmuch emacs mail client without your emacs init file interfering, Run\n\""
|
||||
(file-name-nondirectory load-file-name) "\" with '-q' (or '-Q') as an argument.
|
||||
")
|
||||
(let ((emacs-init-file-name) (notmuch-init-file-name))
|
||||
;; determining init file name in startup.el/command-line is too complicated
|
||||
;; to be duplicated here; these 3 file names covers most of the users
|
||||
(mapc (lambda (fn) (if (file-exists-p fn) (setq emacs-init-file-name fn)))
|
||||
'("~/.emacs.d/init.el" "~/.emacs" "~/.emacs.el"))
|
||||
(setq notmuch-init-file-name "~/.emacs.d/notmuch-config.el")
|
||||
(unless (file-exists-p notmuch-init-file-name)
|
||||
(setq notmuch-init-file-name nil))
|
||||
(if (and emacs-init-file-name notmuch-init-file-name)
|
||||
(insert "
|
||||
If you want to load your initialization files now, evaluate\n* (progn")
|
||||
(if (or emacs-init-file-name notmuch-init-file-name)
|
||||
(insert "
|
||||
If you want to load your initialization file now, evaluate\n*")))
|
||||
(if emacs-init-file-name
|
||||
(insert " (load \"" emacs-init-file-name "\")"))
|
||||
(if notmuch-init-file-name
|
||||
(insert " (load \"" notmuch-init-file-name "\")"))
|
||||
(if (and emacs-init-file-name notmuch-init-file-name)
|
||||
(insert ")"))
|
||||
(if (or emacs-init-file-name notmuch-init-file-name)
|
||||
(insert "\n")))
|
||||
(if (>= emacs-major-version 24)
|
||||
(insert "
|
||||
If you want to use packages (e.g. company from elpa) evaluate
|
||||
* (progn (require 'package) (package-initialize))
|
||||
")))
|
||||
|
||||
(insert "
|
||||
To start notmuch (hello) screen, evaluate
|
||||
* (notmuch-hello)")
|
||||
|
||||
(add-hook 'emacs-startup-hook
|
||||
(lambda ()
|
||||
(with-current-buffer "*scratch*"
|
||||
(lisp-interaction-mode)
|
||||
(goto-char (point-min))
|
||||
(forward-line 2)
|
||||
(set-buffer-modified-p nil))))
|
||||
|
||||
;; Local Variables:
|
||||
;; mode: emacs-lisp
|
||||
;; End:
|
|
@ -12,7 +12,7 @@ master_doc = 'index'
|
|||
|
||||
# General information about the project.
|
||||
project = u'notmuch'
|
||||
copyright = u'2009-2015, Carl Worth and many others'
|
||||
copyright = u'2009-2016, Carl Worth and many others'
|
||||
|
||||
location = os.path.dirname(__file__)
|
||||
|
||||
|
|
|
@ -13,8 +13,10 @@ DESCRIPTION
|
|||
Constructs a reply template for a set of messages.
|
||||
|
||||
To make replying to email easier, **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. Unless
|
||||
set of messages and constructs a suitable mail template. Its To:
|
||||
address is set according to the original email in this way: if the
|
||||
Reply-to: header is present and different from any To:/Cc: address it
|
||||
is used, otherwise From: header is used. Unless
|
||||
``--reply-to=sender`` is specified, values 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
|
||||
|
|
|
@ -20,6 +20,7 @@ emacs_sources := \
|
|||
$(dir)/notmuch-print.el \
|
||||
$(dir)/notmuch-version.el \
|
||||
$(dir)/notmuch-jump.el \
|
||||
$(dir)/notmuch-company.el
|
||||
|
||||
$(dir)/notmuch-version.el: $(dir)/Makefile.local version.stamp
|
||||
$(dir)/notmuch-version.el: $(srcdir)/$(dir)/notmuch-version.el.tmpl
|
||||
|
@ -52,6 +53,10 @@ $(dir)/.eldeps: $(dir)/Makefile.local $(dir)/make-deps.el $(emacs_sources)
|
|||
$(dir)/.eldeps.x: $(dir)/.eldeps
|
||||
@cmp -s $^ $@ || cp $^ $@
|
||||
-include $(dir)/.eldeps.x
|
||||
|
||||
# Add the one dependency make-deps.el does not have visibility to.
|
||||
$(dir)/notmuch-lib.elc: $(dir)/notmuch-version.elc
|
||||
|
||||
endif
|
||||
CLEAN+=$(dir)/.eldeps $(dir)/.eldeps.tmp $(dir)/.eldeps.x
|
||||
|
||||
|
|
|
@ -143,3 +143,5 @@ If no break point is found, return nil."
|
|||
t)))
|
||||
|
||||
(provide 'coolj)
|
||||
|
||||
;;; coolj.el ends here
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
;;
|
||||
;; Authors: Austin Clements <aclements@csail.mit.edu>
|
||||
|
||||
;;; Code:
|
||||
|
||||
(defun batch-make-deps ()
|
||||
"Invoke `make-deps' for each file on the command line."
|
||||
|
||||
|
@ -64,3 +66,5 @@ rules will be given relative to DIR, or `default-directory'."
|
|||
(file-name-sans-extension
|
||||
(file-relative-name fname dir)))))))))
|
||||
(end-of-file nil))))
|
||||
|
||||
;;; make-deps.el ends here
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;; notmuch-address.el --- address completion with notmuch
|
||||
;;; notmuch-address.el --- address completion with notmuch
|
||||
;;
|
||||
;; Copyright © David Edmondson
|
||||
;;
|
||||
|
@ -19,15 +19,24 @@
|
|||
;;
|
||||
;; Authors: David Edmondson <dme@dme.org>
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'message)
|
||||
|
||||
(require 'notmuch-parser)
|
||||
(require 'notmuch-lib)
|
||||
(require 'notmuch-company)
|
||||
;;
|
||||
(declare-function company-manual-begin "company")
|
||||
|
||||
(defcustom notmuch-address-command "notmuch-addresses"
|
||||
(defcustom notmuch-address-command 'internal
|
||||
"The command which generates possible addresses. It must take a
|
||||
single argument and output a list of possible matches, one per
|
||||
line."
|
||||
:type 'string
|
||||
line. The default value of `internal' uses built-in address
|
||||
completion."
|
||||
:type '(radio
|
||||
(const :tag "Use internal address completion" internal)
|
||||
(const :tag "Disable address completion" nil)
|
||||
(string :tag "Use external completion command" "notmuch-addresses"))
|
||||
:group 'notmuch-send
|
||||
:group 'notmuch-external)
|
||||
|
||||
|
@ -42,53 +51,105 @@ to know how address selection is made by default."
|
|||
:group 'notmuch-send
|
||||
:group 'notmuch-external)
|
||||
|
||||
(defvar notmuch-address-last-harvest 0
|
||||
"Time of last address harvest")
|
||||
|
||||
(defvar notmuch-address-completions (make-hash-table :test 'equal)
|
||||
"Hash of email addresses for completion during email composition.
|
||||
This variable is set by calling `notmuch-address-harvest'.")
|
||||
|
||||
(defvar notmuch-address-full-harvest-finished nil
|
||||
"t indicates that full completion address harvesting has been
|
||||
finished")
|
||||
|
||||
(defun notmuch-address-selection-function (prompt collection initial-input)
|
||||
"Call (`completing-read'
|
||||
PROMPT COLLECTION nil nil INITIAL-INPUT 'notmuch-address-history)"
|
||||
(completing-read
|
||||
prompt collection nil nil initial-input 'notmuch-address-history))
|
||||
|
||||
(defvar notmuch-address-message-alist-member
|
||||
'("^\\(Resent-\\)?\\(To\\|B?Cc\\|Reply-To\\|From\\|Mail-Followup-To\\|Mail-Copies-To\\):"
|
||||
. notmuch-address-expand-name))
|
||||
(defvar notmuch-address-completion-headers-regexp
|
||||
"^\\(Resent-\\)?\\(To\\|B?Cc\\|Reply-To\\|From\\|Mail-Followup-To\\|Mail-Copies-To\\):")
|
||||
|
||||
(defvar notmuch-address-history nil)
|
||||
|
||||
(defun notmuch-address-message-insinuate ()
|
||||
(unless (memq notmuch-address-message-alist-member message-completion-alist)
|
||||
(setq message-completion-alist
|
||||
(push notmuch-address-message-alist-member message-completion-alist))))
|
||||
(message "calling notmuch-address-message-insinuate is no longer needed"))
|
||||
|
||||
(defcustom notmuch-address-use-company t
|
||||
"If available, use company mode for address completion"
|
||||
:type 'boolean
|
||||
:group 'notmuch-send)
|
||||
|
||||
(defun notmuch-address-setup ()
|
||||
(let* ((use-company (and notmuch-address-use-company
|
||||
(eq notmuch-address-command 'internal)
|
||||
(require 'company nil t)))
|
||||
(pair (cons notmuch-address-completion-headers-regexp
|
||||
(if use-company
|
||||
#'company-manual-begin
|
||||
#'notmuch-address-expand-name))))
|
||||
(when use-company
|
||||
(notmuch-company-setup))
|
||||
(unless (memq pair message-completion-alist)
|
||||
(setq message-completion-alist
|
||||
(push pair message-completion-alist)))))
|
||||
|
||||
(defun notmuch-address-matching (substring)
|
||||
"Returns a list of completion candidates matching SUBSTRING.
|
||||
The candidates are taken from `notmuch-address-completions'."
|
||||
(let ((candidates)
|
||||
(re (regexp-quote substring)))
|
||||
(maphash (lambda (key val)
|
||||
(when (string-match re key)
|
||||
(push key candidates)))
|
||||
notmuch-address-completions)
|
||||
candidates))
|
||||
|
||||
(defun notmuch-address-options (original)
|
||||
(process-lines notmuch-address-command original))
|
||||
"Returns a list of completion candidates. Uses either
|
||||
elisp-based implementation or older implementation requiring
|
||||
external commands."
|
||||
(cond
|
||||
((eq notmuch-address-command 'internal)
|
||||
(when (not notmuch-address-full-harvest-finished)
|
||||
;; First, run quick synchronous harvest based on what the user
|
||||
;; entered so far
|
||||
(notmuch-address-harvest (format "to:%s*" original) t))
|
||||
(prog1 (notmuch-address-matching original)
|
||||
;; Then start the (potentially long-running) full asynchronous harvest if necessary
|
||||
(notmuch-address-harvest-trigger)))
|
||||
(t
|
||||
(process-lines notmuch-address-command original))))
|
||||
|
||||
(defun notmuch-address-expand-name ()
|
||||
(let* ((end (point))
|
||||
(beg (save-excursion
|
||||
(re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*")
|
||||
(goto-char (match-end 0))
|
||||
(point)))
|
||||
(orig (buffer-substring-no-properties beg end))
|
||||
(completion-ignore-case t)
|
||||
(options (with-temp-message "Looking for completion candidates..."
|
||||
(notmuch-address-options orig)))
|
||||
(num-options (length options))
|
||||
(chosen (cond
|
||||
((eq num-options 0)
|
||||
nil)
|
||||
((eq num-options 1)
|
||||
(car options))
|
||||
(t
|
||||
(funcall notmuch-address-selection-function
|
||||
(format "Address (%s matches): " num-options)
|
||||
(cdr options) (car options))))))
|
||||
(if chosen
|
||||
(progn
|
||||
(push chosen notmuch-address-history)
|
||||
(delete-region beg end)
|
||||
(insert chosen))
|
||||
(message "No matches.")
|
||||
(ding))))
|
||||
(when notmuch-address-command
|
||||
(let* ((end (point))
|
||||
(beg (save-excursion
|
||||
(re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*")
|
||||
(goto-char (match-end 0))
|
||||
(point)))
|
||||
(orig (buffer-substring-no-properties beg end))
|
||||
(completion-ignore-case t)
|
||||
(options (with-temp-message "Looking for completion candidates..."
|
||||
(notmuch-address-options orig)))
|
||||
(num-options (length options))
|
||||
(chosen (cond
|
||||
((eq num-options 0)
|
||||
nil)
|
||||
((eq num-options 1)
|
||||
(car options))
|
||||
(t
|
||||
(funcall notmuch-address-selection-function
|
||||
(format "Address (%s matches): " num-options)
|
||||
(cdr options) (car options))))))
|
||||
(if chosen
|
||||
(progn
|
||||
(push chosen notmuch-address-history)
|
||||
(delete-region beg end)
|
||||
(insert chosen))
|
||||
(message "No matches.")
|
||||
(ding)))))
|
||||
|
||||
;; Copied from `w3m-which-command'.
|
||||
(defun notmuch-address-locate-command (command)
|
||||
|
@ -109,11 +170,85 @@ to know how address selection is made by default."
|
|||
(not (file-directory-p bin))))
|
||||
(throw 'found-command bin))))))))
|
||||
|
||||
;; If we can find the program specified by `notmuch-address-command',
|
||||
;; insinuate ourselves into `message-mode'.
|
||||
(when (notmuch-address-locate-command notmuch-address-command)
|
||||
(notmuch-address-message-insinuate))
|
||||
(defun notmuch-address-harvest-addr (result)
|
||||
(let ((name-addr (plist-get result :name-addr)))
|
||||
(puthash name-addr t notmuch-address-completions)))
|
||||
|
||||
(defun notmuch-address-harvest-handle-result (obj)
|
||||
(notmuch-address-harvest-addr obj))
|
||||
|
||||
(defun notmuch-address-harvest-filter (proc string)
|
||||
(when (buffer-live-p (process-buffer proc))
|
||||
(with-current-buffer (process-buffer proc)
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
(insert string))
|
||||
(notmuch-sexp-parse-partial-list
|
||||
'notmuch-address-harvest-handle-result (process-buffer proc)))))
|
||||
|
||||
(defvar notmuch-address-harvest-procs '(nil . nil)
|
||||
"The currently running harvests.
|
||||
|
||||
The car is a partial harvest, and the cdr is a full harvest")
|
||||
|
||||
(defun notmuch-address-harvest (&optional filter-query synchronous callback)
|
||||
"Collect addresses completion candidates. It queries the
|
||||
notmuch database for all messages sent by the user optionally
|
||||
matching FILTER-QUERY (if not nil). It collects the destination
|
||||
addresses from those messages and stores them in
|
||||
`notmuch-address-completions'. Address harvesting may take some
|
||||
time so the address collection runs asynchronously unless
|
||||
SYNCHRONOUS is t. In case of asynchronous execution, CALLBACK is
|
||||
called when harvesting finishes."
|
||||
(let* ((from-me-query (mapconcat (lambda (x) (concat "from:" x)) (notmuch-user-emails) " or "))
|
||||
(query (if filter-query
|
||||
(format "(%s) and (%s)" from-me-query filter-query)
|
||||
from-me-query))
|
||||
(args `("address" "--format=sexp" "--format-version=2"
|
||||
"--output=recipients"
|
||||
"--deduplicate=address"
|
||||
,query)))
|
||||
(if synchronous
|
||||
(mapc #'notmuch-address-harvest-addr
|
||||
(apply 'notmuch-call-notmuch-sexp args))
|
||||
;; Asynchronous
|
||||
(let* ((current-proc (if filter-query
|
||||
(car notmuch-address-harvest-procs)
|
||||
(cdr notmuch-address-harvest-procs)))
|
||||
(proc-name (format "notmuch-address-%s-harvest"
|
||||
(if filter-query "partial" "full")))
|
||||
(proc-buf (concat " *" proc-name "*")))
|
||||
;; Kill any existing process
|
||||
(when current-proc
|
||||
(kill-buffer (process-buffer current-proc))) ; this also kills the process
|
||||
|
||||
(setq current-proc
|
||||
(apply 'notmuch-start-notmuch proc-name proc-buf
|
||||
callback ; process sentinel
|
||||
args))
|
||||
(set-process-filter current-proc 'notmuch-address-harvest-filter)
|
||||
(set-process-query-on-exit-flag current-proc nil)
|
||||
(if filter-query
|
||||
(setcar notmuch-address-harvest-procs current-proc)
|
||||
(setcdr notmuch-address-harvest-procs current-proc)))))
|
||||
;; return value
|
||||
nil)
|
||||
|
||||
(defun notmuch-address-harvest-trigger ()
|
||||
(let ((now (float-time)))
|
||||
(when (> (- now notmuch-address-last-harvest) 86400)
|
||||
(setq notmuch-address-last-harvest now)
|
||||
(notmuch-address-harvest nil nil
|
||||
(lambda (proc event)
|
||||
;; If harvest fails, we want to try
|
||||
;; again when the trigger is next
|
||||
;; called
|
||||
(if (string= event "finished\n")
|
||||
(setq notmuch-address-full-harvest-finished t)
|
||||
(setq notmuch-address-last-harvest 0)))))))
|
||||
|
||||
;;
|
||||
|
||||
(provide 'notmuch-address)
|
||||
|
||||
;;; notmuch-address.el ends here
|
||||
|
|
88
emacs/notmuch-company.el
Normal file
88
emacs/notmuch-company.el
Normal file
|
@ -0,0 +1,88 @@
|
|||
;;; notmuch-company.el --- Mail address completion for notmuch via company-mode -*- lexical-binding: t -*-
|
||||
|
||||
;; Authors: Trevor Jim <tjim@mac.com>
|
||||
;; Michal Sojka <sojkam1@fel.cvut.cz>
|
||||
;;
|
||||
;; Keywords: mail, completion
|
||||
|
||||
;; This program is free software; you can redistribute it and/or modify
|
||||
;; it under the terms of the GNU General Public License as published by
|
||||
;; 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/>.
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; To enable this, install company mode (https://company-mode.github.io/)
|
||||
;;
|
||||
;; NB company-minimum-prefix-length defaults to 3 so you don't get
|
||||
;; completion unless you type 3 characters
|
||||
|
||||
;;; Code:
|
||||
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(defvar notmuch-company-last-prefix nil)
|
||||
(make-variable-buffer-local 'notmuch-company-last-prefix)
|
||||
(declare-function company-begin-backend "company")
|
||||
(declare-function company-grab "company")
|
||||
(declare-function company-mode "company")
|
||||
(declare-function company-manual-begin "company")
|
||||
(defvar company-backends)
|
||||
|
||||
(declare-function notmuch-address-harvest "notmuch-address")
|
||||
(declare-function notmuch-address-harvest-trigger "notmuch-address")
|
||||
(declare-function notmuch-address-matching "notmuch-address")
|
||||
(defvar notmuch-address-full-harvest-finished)
|
||||
(defvar notmuch-address-completion-headers-regexp)
|
||||
|
||||
;;;###autoload
|
||||
(defun notmuch-company-setup ()
|
||||
(company-mode)
|
||||
(make-local-variable 'company-backends)
|
||||
(setq company-backends '(notmuch-company)))
|
||||
|
||||
;;;###autoload
|
||||
(defun notmuch-company (command &optional arg &rest _ignore)
|
||||
"`company-mode' completion back-end for `notmuch'."
|
||||
(interactive (list 'interactive))
|
||||
(require 'company)
|
||||
(let ((case-fold-search t)
|
||||
(completion-ignore-case t))
|
||||
(case command
|
||||
(interactive (company-begin-backend 'notmuch-company))
|
||||
(prefix (and (derived-mode-p 'message-mode)
|
||||
(looking-back (concat notmuch-address-completion-headers-regexp ".*")
|
||||
(line-beginning-position))
|
||||
(setq notmuch-company-last-prefix (company-grab "[:,][ \t]*\\(.*\\)" 1 (point-at-bol)))))
|
||||
(candidates (cond
|
||||
(notmuch-address-full-harvest-finished
|
||||
;; Update harvested addressed from time to time
|
||||
(notmuch-address-harvest-trigger)
|
||||
(notmuch-address-matching arg))
|
||||
(t
|
||||
(cons :async
|
||||
(lambda (callback)
|
||||
;; First run quick asynchronous harvest based on what the user entered so far
|
||||
(notmuch-address-harvest
|
||||
(format "to:%s*" arg) nil
|
||||
(lambda (_proc _event)
|
||||
(funcall callback (notmuch-address-matching arg))
|
||||
;; Then start the (potentially long-running) full asynchronous harvest if necessary
|
||||
(notmuch-address-harvest-trigger))))))))
|
||||
(match (if (string-match notmuch-company-last-prefix arg)
|
||||
(match-end 0)
|
||||
0))
|
||||
(no-cache t))))
|
||||
|
||||
|
||||
(provide 'notmuch-company)
|
||||
|
||||
;;; notmuch-company.el ends here
|
|
@ -1,4 +1,4 @@
|
|||
;; notmuch-crypto.el --- functions for handling display of cryptographic metadata.
|
||||
;;; notmuch-crypto.el --- functions for handling display of cryptographic metadata.
|
||||
;;
|
||||
;; Copyright © Jameson Rollins
|
||||
;;
|
||||
|
@ -19,6 +19,8 @@
|
|||
;;
|
||||
;; Authors: Jameson Rollins <jrollins@finestructure.net>
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'notmuch-lib)
|
||||
|
||||
(defcustom notmuch-crypto-process-mime nil
|
||||
|
@ -110,8 +112,8 @@ mode."
|
|||
(setq label (concat "Bad signature (claimed key ID " keyid ")"))
|
||||
(setq face 'notmuch-crypto-signature-bad)))
|
||||
(t
|
||||
(setq label "Unknown signature status")
|
||||
(if status (setq label (concat label " \"" status "\"")))))
|
||||
(setq label (concat "Unknown signature status"
|
||||
(if status (concat ": " status))))))
|
||||
(insert-button
|
||||
(concat "[ " label " ]")
|
||||
:type 'notmuch-crypto-status-button-type
|
||||
|
@ -161,7 +163,8 @@ mode."
|
|||
((string= status "bad")
|
||||
(setq label "Decryption error"))
|
||||
(t
|
||||
(setq label (concat "Unknown encstatus \"" status "\""))))
|
||||
(setq label (concat "Unknown encryption status"
|
||||
(if status (concat ": " status))))))
|
||||
(insert-button
|
||||
(concat "[ " label " ]")
|
||||
:type 'notmuch-crypto-status-button-type
|
||||
|
@ -173,3 +176,5 @@ mode."
|
|||
;;
|
||||
|
||||
(provide 'notmuch-crypto)
|
||||
|
||||
;;; notmuch-crypto.el ends here
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;; notmuch-hello.el --- welcome to notmuch, a frontend
|
||||
;;; notmuch-hello.el --- welcome to notmuch, a frontend
|
||||
;;
|
||||
;; Copyright © David Edmondson
|
||||
;;
|
||||
|
@ -19,6 +19,8 @@
|
|||
;;
|
||||
;; Authors: David Edmondson <dme@dme.org>
|
||||
|
||||
;;; Code:
|
||||
|
||||
(eval-when-compile (require 'cl))
|
||||
(require 'widget)
|
||||
(require 'wid-edit) ; For `widget-forward'.
|
||||
|
@ -652,8 +654,12 @@ with `notmuch-hello-query-counts'."
|
|||
|
||||
(defvar notmuch-hello-mode-map
|
||||
(let ((map (if (fboundp 'make-composed-keymap)
|
||||
;; Inherit both widget-keymap and notmuch-common-keymap
|
||||
(make-composed-keymap widget-keymap)
|
||||
;; Inherit both widget-keymap and
|
||||
;; notmuch-common-keymap. We have to use
|
||||
;; make-sparse-keymap to force this to be a new
|
||||
;; keymap (so that when we modify map it does not
|
||||
;; modify widget-keymap).
|
||||
(make-composed-keymap (list (make-sparse-keymap) widget-keymap))
|
||||
;; Before Emacs 24, keymaps didn't support multiple
|
||||
;; inheritance,, so just copy the widget keymap since
|
||||
;; it's unlikely to change.
|
||||
|
@ -668,6 +674,31 @@ with `notmuch-hello-query-counts'."
|
|||
(defun notmuch-hello-mode ()
|
||||
"Major mode for convenient notmuch navigation. This is your entry portal into notmuch.
|
||||
|
||||
Saved searches are \"bookmarks\" for arbitrary queries. Hit RET
|
||||
or click on a saved search to view matching threads. Edit saved
|
||||
searches with the `edit' button. Type `\\[notmuch-jump-search]'
|
||||
in any Notmuch screen for quick access to saved searches that
|
||||
have shortcut keys.
|
||||
|
||||
Type new searches in the search box and hit RET to view matching
|
||||
threads. Hit RET in a recent search box to re-submit a previous
|
||||
search. Edit it first if you like. Save a recent search to saved
|
||||
searches with the `save' button.
|
||||
|
||||
Hit `\\[notmuch-search]' or `\\[notmuch-tree]' in any Notmuch
|
||||
screen to search for messages and view matching threads or
|
||||
messages, respectively. Recent searches are available in the
|
||||
minibuffer history.
|
||||
|
||||
Expand the all tags view with the `show' button (and collapse
|
||||
again with the `hide' button). Hit RET or click on a tag name to
|
||||
view matching threads.
|
||||
|
||||
Hit `\\[notmuch-refresh-this-buffer]' to refresh the screen and
|
||||
`\\[notmuch-bury-or-kill-this-buffer]' to quit.
|
||||
|
||||
The screen may be customized via `\\[customize]'.
|
||||
|
||||
Complete list of currently available key bindings:
|
||||
|
||||
\\{notmuch-hello-mode-map}"
|
||||
|
@ -903,20 +934,19 @@ following:
|
|||
(defun notmuch-hello-insert-footer ()
|
||||
"Insert the notmuch-hello footer."
|
||||
(let ((start (point)))
|
||||
(widget-insert "Type a search query and hit RET to view matching threads.\n")
|
||||
(when notmuch-search-history
|
||||
(widget-insert "Hit RET to re-submit a previous search. Edit it first if you like.\n")
|
||||
(widget-insert "Save recent searches with the `save' button.\n"))
|
||||
(when notmuch-saved-searches
|
||||
(widget-insert "Edit saved searches with the `edit' button.\n"))
|
||||
(widget-insert "Hit RET or click on a saved search or tag name to view matching threads.\n")
|
||||
(widget-insert "`=' to refresh this screen. `s' to search messages. `q' to quit.\n")
|
||||
(widget-insert "Hit `?' for context-sensitive help in any Notmuch screen.\n")
|
||||
(widget-insert "Customize ")
|
||||
(widget-create 'link
|
||||
:notify (lambda (&rest ignore)
|
||||
(customize-group 'notmuch))
|
||||
:button-prefix "" :button-suffix ""
|
||||
"Notmuch")
|
||||
(widget-insert " or ")
|
||||
(widget-create 'link
|
||||
:notify (lambda (&rest ignore)
|
||||
(customize-variable 'notmuch-hello-sections))
|
||||
:button-prefix "" :button-suffix ""
|
||||
"Customize")
|
||||
(widget-insert " this page.")
|
||||
"this page.")
|
||||
(let ((fill-column (- (window-width) notmuch-hello-indent)))
|
||||
(center-region start (point)))))
|
||||
|
||||
|
@ -988,3 +1018,5 @@ following:
|
|||
;;
|
||||
|
||||
(provide 'notmuch-hello)
|
||||
|
||||
;;; notmuch-hello.el ends here
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;; notmuch-jump.el --- User-friendly shortcut keys
|
||||
;;; notmuch-jump.el --- User-friendly shortcut keys
|
||||
;;
|
||||
;; Copyright © Austin Clements
|
||||
;;
|
||||
|
@ -20,6 +20,8 @@
|
|||
;; Authors: Austin Clements <aclements@csail.mit.edu>
|
||||
;; David Edmondson <dme@dme.org>
|
||||
|
||||
;;; Code:
|
||||
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(require 'notmuch-lib)
|
||||
|
@ -176,3 +178,5 @@ buffer."
|
|||
;;
|
||||
|
||||
(provide 'notmuch-jump)
|
||||
|
||||
;;; notmuch-jump.el ends here
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;; notmuch-lib.el --- common variables, functions and function declarations
|
||||
;;; notmuch-lib.el --- common variables, functions and function declarations
|
||||
;;
|
||||
;; Copyright © Carl Worth
|
||||
;;
|
||||
|
@ -21,6 +21,8 @@
|
|||
|
||||
;; This is an part of an emacs-based interface to the notmuch mail system.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'mm-view)
|
||||
(require 'mm-decode)
|
||||
(require 'cl)
|
||||
|
@ -232,6 +234,9 @@ on the command line, and then retry your notmuch command")))
|
|||
"Return the user.other_email value (as a list) from the notmuch configuration."
|
||||
(split-string (notmuch-config-get "user.other_email") "\n" t))
|
||||
|
||||
(defun notmuch-user-emails ()
|
||||
(cons (notmuch-user-primary-email) (notmuch-user-other-email)))
|
||||
|
||||
(defun notmuch-poll ()
|
||||
"Run \"notmuch new\" or an external script to import mail.
|
||||
|
||||
|
@ -240,8 +245,9 @@ depending on the value of `notmuch-poll-script'."
|
|||
(interactive)
|
||||
(if (stringp notmuch-poll-script)
|
||||
(unless (string= notmuch-poll-script "")
|
||||
(call-process notmuch-poll-script nil nil))
|
||||
(call-process notmuch-command nil nil nil "new")))
|
||||
(unless (equal (call-process notmuch-poll-script nil nil) 0)
|
||||
(error "Notmuch: poll script `%s' failed!" notmuch-poll-script)))
|
||||
(notmuch-call-notmuch-process "new")))
|
||||
|
||||
(defun notmuch-bury-or-kill-this-buffer ()
|
||||
"Undisplay the current buffer.
|
||||
|
@ -516,11 +522,23 @@ This replaces spaces, percents, and double quotes in STR with
|
|||
"multipart/related"
|
||||
))
|
||||
|
||||
(defun notmuch-multipart/alternative-choose (types)
|
||||
"Return a list of preferred types from the given list of types"
|
||||
(defun notmuch-multipart/alternative-determine-discouraged (msg)
|
||||
"Return the discouraged alternatives for the specified message."
|
||||
;; If a function, return the result of calling it.
|
||||
(if (functionp notmuch-multipart/alternative-discouraged)
|
||||
(funcall notmuch-multipart/alternative-discouraged msg)
|
||||
;; Otherwise simply return the value of the variable, which is
|
||||
;; assumed to be a list of discouraged alternatives. This is the
|
||||
;; default behaviour.
|
||||
notmuch-multipart/alternative-discouraged))
|
||||
|
||||
(defun notmuch-multipart/alternative-choose (msg types)
|
||||
"Return a list of preferred types from the given list of types
|
||||
for this message, if present."
|
||||
;; Based on `mm-preferred-alternative-precedence'.
|
||||
(let ((seq types))
|
||||
(dolist (pref (reverse notmuch-multipart/alternative-discouraged))
|
||||
(let ((discouraged (notmuch-multipart/alternative-determine-discouraged msg))
|
||||
(seq types))
|
||||
(dolist (pref (reverse discouraged))
|
||||
(dolist (elem (copy-sequence seq))
|
||||
(when (string-match pref elem)
|
||||
(setq seq (nconc (delete elem seq) (list elem))))))
|
||||
|
@ -533,6 +551,34 @@ the given type."
|
|||
(lambda (part) (notmuch-match-content-type (plist-get part :content-type) type))
|
||||
parts))
|
||||
|
||||
(defun notmuch--get-bodypart-raw (msg part process-crypto binaryp cache)
|
||||
(let* ((plist-elem (if binaryp :content-binary :content))
|
||||
(data (or (plist-get part plist-elem)
|
||||
(with-temp-buffer
|
||||
;; Emacs internally uses a UTF-8-like multibyte string
|
||||
;; representation by default (regardless of the coding
|
||||
;; system, which only affects how it goes from outside data
|
||||
;; to this internal representation). This *almost* never
|
||||
;; matters. Annoyingly, it does matter if we use this data
|
||||
;; in an image descriptor, since Emacs will use its internal
|
||||
;; data buffer directly and this multibyte representation
|
||||
;; corrupts binary image formats. Since the caller is
|
||||
;; asking for binary data, a unibyte string is a more
|
||||
;; appropriate representation anyway.
|
||||
(when binaryp
|
||||
(set-buffer-multibyte nil))
|
||||
(let ((args `("show" "--format=raw"
|
||||
,(format "--part=%s" (plist-get part :id))
|
||||
,@(when process-crypto '("--decrypt"))
|
||||
,(notmuch-id-to-query (plist-get msg :id))))
|
||||
(coding-system-for-read
|
||||
(if binaryp 'no-conversion 'utf-8)))
|
||||
(apply #'call-process notmuch-command nil '(t nil) nil args)
|
||||
(buffer-string))))))
|
||||
(when (and cache data)
|
||||
(plist-put part plist-elem data))
|
||||
data))
|
||||
|
||||
(defun notmuch-get-bodypart-binary (msg part process-crypto &optional cache)
|
||||
"Return the unprocessed content of PART in MSG as a unibyte string.
|
||||
|
||||
|
@ -543,57 +589,18 @@ this does no charset conversion.
|
|||
|
||||
If CACHE is non-nil, the content of this part will be saved in
|
||||
MSG (if it isn't already)."
|
||||
(let ((data (plist-get part :binary-content)))
|
||||
(when (not data)
|
||||
(let ((args `("show" "--format=raw"
|
||||
,(format "--part=%d" (plist-get part :id))
|
||||
,@(when process-crypto '("--decrypt"))
|
||||
,(notmuch-id-to-query (plist-get msg :id)))))
|
||||
(with-temp-buffer
|
||||
;; Emacs internally uses a UTF-8-like multibyte string
|
||||
;; representation by default (regardless of the coding
|
||||
;; system, which only affects how it goes from outside data
|
||||
;; to this internal representation). This *almost* never
|
||||
;; matters. Annoyingly, it does matter if we use this data
|
||||
;; in an image descriptor, since Emacs will use its internal
|
||||
;; data buffer directly and this multibyte representation
|
||||
;; corrupts binary image formats. Since the caller is
|
||||
;; asking for binary data, a unibyte string is a more
|
||||
;; appropriate representation anyway.
|
||||
(set-buffer-multibyte nil)
|
||||
(let ((coding-system-for-read 'no-conversion))
|
||||
(apply #'call-process notmuch-command nil '(t nil) nil args)
|
||||
(setq data (buffer-string)))))
|
||||
(when cache
|
||||
;; Cheat. part is non-nil, and `plist-put' always modifies
|
||||
;; the list in place if it's non-nil.
|
||||
(plist-put part :binary-content data)))
|
||||
data))
|
||||
(notmuch--get-bodypart-raw msg part process-crypto t cache))
|
||||
|
||||
(defun notmuch-get-bodypart-text (msg part process-crypto &optional cache)
|
||||
"Return the text content of PART in MSG.
|
||||
|
||||
This returns the content of the given part as a multibyte Lisp
|
||||
string after performing content transfer decoding and any
|
||||
necessary charset decoding. It is an error to use this for
|
||||
non-text/* parts.
|
||||
necessary charset decoding.
|
||||
|
||||
If CACHE is non-nil, the content of this part will be saved in
|
||||
MSG (if it isn't already)."
|
||||
(let ((content (plist-get part :content)))
|
||||
(when (not content)
|
||||
;; Use show --format=sexp to fetch decoded content
|
||||
(let* ((args `("show" "--format=sexp" "--include-html"
|
||||
,(format "--part=%s" (plist-get part :id))
|
||||
,@(when process-crypto '("--decrypt"))
|
||||
,(notmuch-id-to-query (plist-get msg :id))))
|
||||
(npart (apply #'notmuch-call-notmuch-sexp args)))
|
||||
(setq content (plist-get npart :content))
|
||||
(when (not content)
|
||||
(error "Internal error: No :content from %S" args)))
|
||||
(when cache
|
||||
(plist-put part :content content)))
|
||||
content))
|
||||
(notmuch--get-bodypart-raw msg part process-crypto nil cache))
|
||||
|
||||
;; Workaround: The call to `mm-display-part' below triggers a bug in
|
||||
;; Emacs 24 if it attempts to use the shr renderer to display an HTML
|
||||
|
@ -926,3 +933,5 @@ status."
|
|||
;; Local Variables:
|
||||
;; byte-compile-warnings: (not cl-functions)
|
||||
;; End:
|
||||
|
||||
;;; notmuch-lib.el ends here
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
;;; notmuch-maildir-fcc.el ---
|
||||
|
||||
;; This file 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 2, or (at your
|
||||
|
@ -12,10 +14,14 @@
|
|||
;; along with GNU Emacs; see the file COPYING. If not, write to the
|
||||
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
;; Boston, MA 02110-1301, USA.
|
||||
;;
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; To use this as the fcc handler for message-mode,
|
||||
;; customize the notmuch-fcc-dirs variable
|
||||
|
||||
;;; Code:
|
||||
|
||||
(eval-when-compile (require 'cl))
|
||||
(require 'message)
|
||||
|
||||
|
@ -211,3 +217,4 @@ return t if successful, and nil otherwise."
|
|||
|
||||
(provide 'notmuch-maildir-fcc)
|
||||
|
||||
;;; notmuch-maildir-fcc.el ends here
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;; notmuch-message.el --- message-mode functions specific to notmuch
|
||||
;;; notmuch-message.el --- message-mode functions specific to notmuch
|
||||
;;
|
||||
;; Copyright © Jesse Rosenthal
|
||||
;;
|
||||
|
@ -19,6 +19,8 @@
|
|||
;;
|
||||
;; Authors: Jesse Rosenthal <jrosenthal@jhu.edu>
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'message)
|
||||
(require 'notmuch-tag)
|
||||
(require 'notmuch-mua)
|
||||
|
@ -46,3 +48,5 @@ the \"inbox\" and \"todo\" tags, you would set:
|
|||
(add-hook 'message-send-hook 'notmuch-message-mark-replied)
|
||||
|
||||
(provide 'notmuch-message)
|
||||
|
||||
;;; notmuch-message.el ends here
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;; notmuch-mua.el --- emacs style mail-user-agent
|
||||
;;; notmuch-mua.el --- emacs style mail-user-agent
|
||||
;;
|
||||
;; Copyright © David Edmondson
|
||||
;;
|
||||
|
@ -19,6 +19,8 @@
|
|||
;;
|
||||
;; Authors: David Edmondson <dme@dme.org>
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'message)
|
||||
(require 'mm-view)
|
||||
(require 'format-spec)
|
||||
|
@ -28,7 +30,9 @@
|
|||
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(declare-function notmuch-show-insert-bodypart "notmuch-show" (msg part depth &optional hide))
|
||||
(declare-function notmuch-show-insert-body "notmuch-show" (msg body depth))
|
||||
(declare-function notmuch-fcc-header-setup "notmuch-maildir-fcc" ())
|
||||
(declare-function notmuch-fcc-handler "notmuch-maildir-fcc" (destdir))
|
||||
|
||||
;;
|
||||
|
||||
|
@ -91,6 +95,23 @@ Note that these functions use `mail-citation-hook' if that is non-nil."
|
|||
:link '(custom-manual "(message)Insertion Variables")
|
||||
:group 'notmuch-reply)
|
||||
|
||||
(defcustom notmuch-mua-reply-insert-header-p-function
|
||||
'notmuch-show-reply-insert-header-p-never
|
||||
"Function to decide which parts get a header when replying.
|
||||
|
||||
This function specifies which parts of a mime message with
|
||||
mutiple parts get a header."
|
||||
:type '(radio (const :tag "No part headers"
|
||||
notmuch-show-reply-insert-header-p-never)
|
||||
(const :tag "All except multipart/* and hidden parts"
|
||||
notmuch-show-reply-insert-header-p-trimmed)
|
||||
(const :tag "Only for included text parts"
|
||||
notmuch-show-reply-insert-header-p-minimal)
|
||||
(const :tag "Exactly as in show view"
|
||||
notmuch-show-insert-header-p)
|
||||
(function :tag "Other"))
|
||||
:group 'notmuch-reply)
|
||||
|
||||
;;
|
||||
|
||||
(defun notmuch-mua-get-switch-function ()
|
||||
|
@ -142,31 +163,6 @@ Note that these functions use `mail-citation-hook' if that is non-nil."
|
|||
else if (notmuch-match-content-type (plist-get part :content-type) "multipart/*")
|
||||
do (notmuch-mua-reply-crypto (plist-get part :content))))
|
||||
|
||||
(defun notmuch-mua-get-quotable-parts (parts)
|
||||
(loop for part in parts
|
||||
if (notmuch-match-content-type (plist-get part :content-type) "multipart/alternative")
|
||||
collect (let* ((subparts (plist-get part :content))
|
||||
(types (mapcar (lambda (part) (plist-get part :content-type)) subparts))
|
||||
(chosen-type (car (notmuch-multipart/alternative-choose types))))
|
||||
(loop for part in (reverse subparts)
|
||||
if (notmuch-match-content-type (plist-get part :content-type) chosen-type)
|
||||
return part))
|
||||
else if (notmuch-match-content-type (plist-get part :content-type) "multipart/*")
|
||||
append (notmuch-mua-get-quotable-parts (plist-get part :content))
|
||||
else if (notmuch-match-content-type (plist-get part :content-type) "text/*")
|
||||
collect part))
|
||||
|
||||
(defun notmuch-mua-insert-quotable-part (message part)
|
||||
;; We don't want text properties leaking from the show renderer into
|
||||
;; the reply so we use a temp buffer. Also we don't want hooks, such
|
||||
;; as notmuch-wash-*, to be run on the quotable part so we set
|
||||
;; notmuch-show-insert-text/plain-hook to nil.
|
||||
(insert (with-temp-buffer
|
||||
(let ((notmuch-show-insert-text/plain-hook nil))
|
||||
;; Show the part but do not add buttons.
|
||||
(notmuch-show-insert-bodypart message part 0 'no-buttons))
|
||||
(buffer-substring-no-properties (point-min) (point-max)))))
|
||||
|
||||
;; There is a bug in emacs 23's message.el that results in a newline
|
||||
;; not being inserted after the References header, so the next header
|
||||
;; is concatenated to the end of it. This function fixes the problem,
|
||||
|
@ -245,10 +241,20 @@ Note that these functions use `mail-citation-hook' if that is non-nil."
|
|||
(insert "From: " from "\n")
|
||||
(insert "Date: " date "\n\n")
|
||||
|
||||
;; Get the parts of the original message that should be quoted; this includes
|
||||
;; all the text parts, except the non-preferred ones in a multipart/alternative.
|
||||
(let ((quotable-parts (notmuch-mua-get-quotable-parts (plist-get original :body))))
|
||||
(mapc (apply-partially 'notmuch-mua-insert-quotable-part original) quotable-parts))
|
||||
(insert (with-temp-buffer
|
||||
(let
|
||||
;; Don't attempt to clean up messages, excerpt
|
||||
;; citations, etc. in the original message before
|
||||
;; quoting.
|
||||
((notmuch-show-insert-text/plain-hook nil)
|
||||
;; Don't omit long parts.
|
||||
(notmuch-show-max-text-part-size 0)
|
||||
;; Insert headers for parts as appropriate for replying.
|
||||
(notmuch-show-insert-header-p-function notmuch-mua-reply-insert-header-p-function)
|
||||
;; Don't indent multipart sub-parts.
|
||||
(notmuch-show-indent-multipart nil))
|
||||
(notmuch-show-insert-body original (plist-get original :body) 0)
|
||||
(buffer-substring-no-properties (point-min) (point-max)))))
|
||||
|
||||
(set-mark (point))
|
||||
(goto-char start)
|
||||
|
@ -269,15 +275,45 @@ Note that these functions use `mail-citation-hook' if that is non-nil."
|
|||
(set-buffer-modified-p nil))
|
||||
|
||||
(define-derived-mode notmuch-message-mode message-mode "Message[Notmuch]"
|
||||
"Notmuch message composition mode. Mostly like `message-mode'")
|
||||
"Notmuch message composition mode. Mostly like `message-mode'"
|
||||
(when notmuch-address-command
|
||||
(notmuch-address-setup)))
|
||||
|
||||
(put 'notmuch-message-mode 'flyspell-mode-predicate 'mail-mode-flyspell-verify)
|
||||
|
||||
(define-key notmuch-message-mode-map (kbd "C-c C-c") #'notmuch-mua-send-and-exit)
|
||||
(define-key notmuch-message-mode-map (kbd "C-c C-s") #'notmuch-mua-send)
|
||||
|
||||
(defun notmuch-mua-mail (&optional to subject other-headers &rest other-args)
|
||||
"Invoke the notmuch mail composition window.
|
||||
(defun notmuch-mua-pop-to-buffer (name switch-function)
|
||||
"Pop to buffer NAME, and warn if it already exists and is
|
||||
modified. This function is notmuch addaptation of
|
||||
`message-pop-to-buffer'."
|
||||
(let ((buffer (get-buffer name)))
|
||||
(if (and buffer
|
||||
(buffer-name buffer))
|
||||
(let ((window (get-buffer-window buffer 0)))
|
||||
(if window
|
||||
;; Raise the frame already displaying the message buffer.
|
||||
(progn
|
||||
(gnus-select-frame-set-input-focus (window-frame window))
|
||||
(select-window window))
|
||||
(funcall switch-function buffer)
|
||||
(set-buffer buffer))
|
||||
(when (and (buffer-modified-p)
|
||||
(not (prog1
|
||||
(y-or-n-p
|
||||
"Message already being composed; erase? ")
|
||||
(message nil))))
|
||||
(error "Message being composed")))
|
||||
(funcall switch-function name)
|
||||
(set-buffer name))
|
||||
(erase-buffer)
|
||||
(notmuch-message-mode)))
|
||||
|
||||
OTHER-ARGS are passed through to `message-mail'."
|
||||
(defun notmuch-mua-mail (&optional to subject other-headers continue
|
||||
switch-function yank-action send-actions
|
||||
return-action &rest ignored)
|
||||
"Invoke the notmuch mail composition window."
|
||||
(interactive)
|
||||
|
||||
(when notmuch-mua-user-agent-function
|
||||
|
@ -286,11 +322,29 @@ OTHER-ARGS are passed through to `message-mail'."
|
|||
(push (cons 'User-Agent user-agent) other-headers))))
|
||||
|
||||
(unless (assq 'From other-headers)
|
||||
(push (cons 'From (concat
|
||||
(notmuch-user-name) " <" (notmuch-user-primary-email) ">")) other-headers))
|
||||
(push (cons 'From (message-make-from
|
||||
(notmuch-user-name) (notmuch-user-primary-email))) other-headers))
|
||||
|
||||
(apply #'message-mail to subject other-headers other-args)
|
||||
(notmuch-message-mode)
|
||||
(notmuch-mua-pop-to-buffer (message-buffer-name "mail" to)
|
||||
(or switch-function (notmuch-mua-get-switch-function)))
|
||||
(let ((headers
|
||||
(append
|
||||
;; The following is copied from `message-mail'
|
||||
`((To . ,(or to "")) (Subject . ,(or subject "")))
|
||||
;; C-h f compose-mail says that headers should be specified as
|
||||
;; (string . value); however all the rest of message expects
|
||||
;; headers to be symbols, not strings (eg message-header-format-alist).
|
||||
;; http://lists.gnu.org/archive/html/emacs-devel/2011-01/msg00337.html
|
||||
;; We need to convert any string input, eg from rmail-start-mail.
|
||||
(dolist (h other-headers other-headers)
|
||||
(if (stringp (car h)) (setcar h (intern (capitalize (car h))))))))
|
||||
(args (list yank-action send-actions)))
|
||||
;; message-setup-1 in Emacs 23 does not accept return-action
|
||||
;; argument. Pass it only if it is supplied by the caller. This
|
||||
;; will never be the case when we're called by `compose-mail' in
|
||||
;; Emacs 23.
|
||||
(when return-action (nconc args '(return-action)))
|
||||
(apply 'message-setup-1 headers args))
|
||||
(notmuch-fcc-header-setup)
|
||||
(message-sort-headers)
|
||||
(message-hide-headers)
|
||||
|
@ -343,7 +397,7 @@ the From: header is already filled in by notmuch."
|
|||
(ido-completing-read (concat "Sender address for " name ": ") addrs
|
||||
nil nil nil 'notmuch-mua-sender-history
|
||||
(car addrs))))
|
||||
(concat name " <" address ">"))))
|
||||
(message-make-from name address))))
|
||||
|
||||
(put 'notmuch-mua-new-mail 'notmuch-prefix-doc "... and prompt for sender")
|
||||
(defun notmuch-mua-new-mail (&optional prompt-for-sender)
|
||||
|
@ -357,25 +411,53 @@ the From: address first."
|
|||
(list (cons 'From (notmuch-mua-prompt-for-sender))))))
|
||||
(notmuch-mua-mail nil nil other-headers nil (notmuch-mua-get-switch-function))))
|
||||
|
||||
(defun notmuch-mua-new-forward-message (&optional prompt-for-sender)
|
||||
"Invoke the notmuch message forwarding window.
|
||||
(defun notmuch-mua-new-forward-messages (messages &optional prompt-for-sender)
|
||||
"Compose a new message forwarding MESSAGES.
|
||||
|
||||
The current buffer must contain an RFC2822 message to forward.
|
||||
|
||||
If PROMPT-FOR-SENDER is non-nil, the user will be prompted for
|
||||
the From: address first."
|
||||
(let* ((cur (current-buffer))
|
||||
(message-forward-decoded-p nil)
|
||||
(subject (message-make-forward-subject))
|
||||
(other-headers
|
||||
If PROMPT-FOR-SENDER is non-nil, the user will be prompteed for
|
||||
the From: address."
|
||||
(let* ((other-headers
|
||||
(when (or prompt-for-sender notmuch-always-prompt-for-sender)
|
||||
(list (cons 'From (notmuch-mua-prompt-for-sender))))))
|
||||
(notmuch-mua-mail nil subject other-headers nil (notmuch-mua-get-switch-function))
|
||||
(message-forward-make-body cur)
|
||||
;; `message-forward-make-body' shows the User-agent header. Hide
|
||||
;; it again.
|
||||
(message-hide-headers)
|
||||
(set-buffer-modified-p nil)))
|
||||
(list (cons 'From (notmuch-mua-prompt-for-sender)))))
|
||||
forward-subject) ;; Comes from the first message and is
|
||||
;; applied later.
|
||||
|
||||
;; Generate the template for the outgoing message.
|
||||
(notmuch-mua-mail nil "" other-headers nil (notmuch-mua-get-switch-function))
|
||||
|
||||
(save-excursion
|
||||
;; Insert all of the forwarded messages.
|
||||
(mapc (lambda (id)
|
||||
(let ((temp-buffer (get-buffer-create
|
||||
(concat "*notmuch-fwd-raw-" id "*"))))
|
||||
;; Get the raw version of this message in the buffer.
|
||||
(with-current-buffer temp-buffer
|
||||
(erase-buffer)
|
||||
(let ((coding-system-for-read 'no-conversion))
|
||||
(call-process notmuch-command nil t nil "show" "--format=raw" id))
|
||||
;; Because we process the messages in reverse order,
|
||||
;; always generate a forwarded subject, then use the
|
||||
;; last (i.e. first) one.
|
||||
(setq forward-subject (message-make-forward-subject)))
|
||||
;; Make a copy ready to be forwarded in the
|
||||
;; composition buffer.
|
||||
(message-forward-make-body temp-buffer)
|
||||
;; Kill the temporary buffer.
|
||||
(kill-buffer temp-buffer)))
|
||||
;; `message-forward-make-body' always puts the message at
|
||||
;; the top, so do them in reverse order.
|
||||
(reverse messages))
|
||||
|
||||
;; Add in the appropriate subject.
|
||||
(save-restriction
|
||||
(message-narrow-to-headers)
|
||||
(message-remove-header "Subject")
|
||||
(message-add-header (concat "Subject: " forward-subject)))
|
||||
|
||||
;; `message-forward-make-body' shows the User-agent header. Hide
|
||||
;; it again.
|
||||
(message-hide-headers)
|
||||
(set-buffer-modified-p nil))))
|
||||
|
||||
(defun notmuch-mua-new-reply (query-string &optional prompt-for-sender reply-all)
|
||||
"Compose a reply to the message identified by QUERY-STRING.
|
||||
|
@ -435,3 +517,5 @@ simply runs the corresponding `message-mode' hook functions."
|
|||
;;
|
||||
|
||||
(provide 'notmuch-mua)
|
||||
|
||||
;;; notmuch-mua.el ends here
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;; notmuch-parser.el --- streaming S-expression parser
|
||||
;;; notmuch-parser.el --- streaming S-expression parser
|
||||
;;
|
||||
;; Copyright © Austin Clements
|
||||
;;
|
||||
|
@ -19,6 +19,8 @@
|
|||
;;
|
||||
;; Authors: Austin Clements <aclements@csail.mit.edu>
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'cl)
|
||||
|
||||
(defun notmuch-sexp-create-parser ()
|
||||
|
@ -205,3 +207,5 @@ move point in the input buffer."
|
|||
;; Local Variables:
|
||||
;; byte-compile-warnings: (not cl-functions)
|
||||
;; End:
|
||||
|
||||
;;; notmuch-parser.el ends here
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;; notmuch-print.el --- printing messages from notmuch.
|
||||
;;; notmuch-print.el --- printing messages from notmuch.
|
||||
;;
|
||||
;; Copyright © David Edmondson
|
||||
;;
|
||||
|
@ -19,6 +19,8 @@
|
|||
;;
|
||||
;; Authors: David Edmondson <dme@dme.org>
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'notmuch-lib)
|
||||
|
||||
(declare-function notmuch-show-get-prop "notmuch-show" (prop &optional props))
|
||||
|
@ -90,3 +92,5 @@ Optional OUTPUT allows passing a list of flags to muttprint."
|
|||
(funcall notmuch-print-mechanism msg))
|
||||
|
||||
(provide 'notmuch-print)
|
||||
|
||||
;;; notmuch-print.el ends here
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;; notmuch-query.el --- provide an emacs api to query notmuch
|
||||
;;; notmuch-query.el --- provide an emacs api to query notmuch
|
||||
;;
|
||||
;; Copyright © David Bremner
|
||||
;;
|
||||
|
@ -19,6 +19,8 @@
|
|||
;;
|
||||
;; Authors: David Bremner <david@tethera.net>
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'notmuch-lib)
|
||||
|
||||
(defun notmuch-query-get-threads (search-terms)
|
||||
|
@ -74,3 +76,5 @@ See the function notmuch-query-get-threads for more information."
|
|||
(notmuch-query-get-threads search-terms)))
|
||||
|
||||
(provide 'notmuch-query)
|
||||
|
||||
;;; notmuch-query.el ends here
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;; notmuch-show.el --- displaying notmuch forests.
|
||||
;;; notmuch-show.el --- displaying notmuch forests.
|
||||
;;
|
||||
;; Copyright © Carl Worth
|
||||
;; Copyright © David Edmondson
|
||||
|
@ -21,6 +21,8 @@
|
|||
;; Authors: Carl Worth <cworth@cworth.org>
|
||||
;; David Edmondson <dme@dme.org>
|
||||
|
||||
;;; Code:
|
||||
|
||||
(eval-when-compile (require 'cl))
|
||||
(require 'mm-view)
|
||||
(require 'message)
|
||||
|
@ -153,27 +155,21 @@ indentation."
|
|||
|
||||
(defvar notmuch-show-thread-id nil)
|
||||
(make-variable-buffer-local 'notmuch-show-thread-id)
|
||||
(put 'notmuch-show-thread-id 'permanent-local t)
|
||||
|
||||
(defvar notmuch-show-parent-buffer nil)
|
||||
(make-variable-buffer-local 'notmuch-show-parent-buffer)
|
||||
(put 'notmuch-show-parent-buffer 'permanent-local t)
|
||||
|
||||
(defvar notmuch-show-query-context nil)
|
||||
(make-variable-buffer-local 'notmuch-show-query-context)
|
||||
(put 'notmuch-show-query-context 'permanent-local t)
|
||||
|
||||
(defvar notmuch-show-process-crypto nil)
|
||||
(make-variable-buffer-local 'notmuch-show-process-crypto)
|
||||
(put 'notmuch-show-process-crypto 'permanent-local t)
|
||||
|
||||
(defvar notmuch-show-elide-non-matching-messages nil)
|
||||
(make-variable-buffer-local 'notmuch-show-elide-non-matching-messages)
|
||||
(put 'notmuch-show-elide-non-matching-messages 'permanent-local t)
|
||||
|
||||
(defvar notmuch-show-indent-content t)
|
||||
(make-variable-buffer-local 'notmuch-show-indent-content)
|
||||
(put 'notmuch-show-indent-content 'permanent-local t)
|
||||
|
||||
(defvar notmuch-show-attachment-debug nil
|
||||
"If t log stdout and stderr from attachment handlers
|
||||
|
@ -353,8 +349,6 @@ operation on the contents of the current buffer."
|
|||
'message-header-cc)
|
||||
((looking-at "[Ss]ubject:")
|
||||
'message-header-subject)
|
||||
((looking-at "[Ff]rom:")
|
||||
'message-header-from)
|
||||
(t
|
||||
'message-header-other))))
|
||||
|
||||
|
@ -509,36 +503,37 @@ message at DEPTH in the current thread."
|
|||
|
||||
(defun notmuch-show-toggle-part-invisibility (&optional button)
|
||||
(interactive)
|
||||
(let* ((button (or button (button-at (point))))
|
||||
(overlay (button-get button 'overlay))
|
||||
(lazy-part (button-get button :notmuch-lazy-part)))
|
||||
;; We have a part to toggle if there is an overlay or if there is a lazy part.
|
||||
;; If neither is present we cannot toggle the part so we just return nil.
|
||||
(when (or overlay lazy-part)
|
||||
(let* ((show (button-get button :notmuch-part-hidden))
|
||||
(new-start (button-start button))
|
||||
(button-label (button-get button :base-label))
|
||||
(old-point (point))
|
||||
(properties (text-properties-at (button-start button)))
|
||||
(inhibit-read-only t))
|
||||
;; Toggle the button itself.
|
||||
(button-put button :notmuch-part-hidden (not show))
|
||||
(goto-char new-start)
|
||||
(insert "[ " button-label (if show " ]" " (hidden) ]"))
|
||||
(set-text-properties new-start (point) properties)
|
||||
(let ((old-end (button-end button)))
|
||||
(move-overlay button new-start (point))
|
||||
(delete-region (point) old-end))
|
||||
(goto-char (min old-point (1- (button-end button))))
|
||||
;; Return nil if there is a lazy-part, it is empty, and we are
|
||||
;; trying to show it. In all other cases return t.
|
||||
(if lazy-part
|
||||
(when show
|
||||
(button-put button :notmuch-lazy-part nil)
|
||||
(notmuch-show-lazy-part lazy-part button))
|
||||
;; else there must be an overlay.
|
||||
(overlay-put overlay 'invisible (not show))
|
||||
t)))))
|
||||
(let ((button (or button (button-at (point)))))
|
||||
(when button
|
||||
(let ((overlay (button-get button 'overlay))
|
||||
(lazy-part (button-get button :notmuch-lazy-part)))
|
||||
;; We have a part to toggle if there is an overlay or if there is a lazy part.
|
||||
;; If neither is present we cannot toggle the part so we just return nil.
|
||||
(when (or overlay lazy-part)
|
||||
(let* ((show (button-get button :notmuch-part-hidden))
|
||||
(new-start (button-start button))
|
||||
(button-label (button-get button :base-label))
|
||||
(old-point (point))
|
||||
(properties (text-properties-at (button-start button)))
|
||||
(inhibit-read-only t))
|
||||
;; Toggle the button itself.
|
||||
(button-put button :notmuch-part-hidden (not show))
|
||||
(goto-char new-start)
|
||||
(insert "[ " button-label (if show " ]" " (hidden) ]"))
|
||||
(set-text-properties new-start (point) properties)
|
||||
(let ((old-end (button-end button)))
|
||||
(move-overlay button new-start (point))
|
||||
(delete-region (point) old-end))
|
||||
(goto-char (min old-point (1- (button-end button))))
|
||||
;; Return nil if there is a lazy-part, it is empty, and we are
|
||||
;; trying to show it. In all other cases return t.
|
||||
(if lazy-part
|
||||
(when show
|
||||
(button-put button :notmuch-lazy-part nil)
|
||||
(notmuch-show-lazy-part lazy-part button))
|
||||
;; else there must be an overlay.
|
||||
(overlay-put overlay 'invisible (not show))
|
||||
t)))))))
|
||||
|
||||
;; Part content ID handling
|
||||
|
||||
|
@ -614,7 +609,7 @@ will return nil if the CID is unknown or cannot be retrieved."
|
|||
(plist-get part :content)))
|
||||
|
||||
(defun notmuch-show-insert-part-multipart/alternative (msg part content-type nth depth button)
|
||||
(let ((chosen-type (car (notmuch-multipart/alternative-choose (notmuch-show-multipart/*-to-list part))))
|
||||
(let ((chosen-type (car (notmuch-multipart/alternative-choose msg (notmuch-show-multipart/*-to-list part))))
|
||||
(inner-parts (plist-get part :content))
|
||||
(start (point)))
|
||||
;; This inserts all parts of the chosen type rather than just one,
|
||||
|
@ -647,14 +642,12 @@ will return nil if the CID is unknown or cannot be retrieved."
|
|||
t)
|
||||
|
||||
(defun notmuch-show-insert-part-multipart/signed (msg part content-type nth depth button)
|
||||
(button-put button 'face 'notmuch-crypto-part-header)
|
||||
;; add signature status button if sigstatus provided
|
||||
(if (plist-member part :sigstatus)
|
||||
(let* ((from (notmuch-show-get-header :From msg))
|
||||
(sigstatus (car (plist-get part :sigstatus))))
|
||||
(notmuch-crypto-insert-sigstatus-button sigstatus from))
|
||||
;; if we're not adding sigstatus, tell the user how they can get it
|
||||
(button-put button 'help-echo "Set notmuch-crypto-process-mime to process cryptographic MIME parts."))
|
||||
(when button
|
||||
(button-put button 'face 'notmuch-crypto-part-header))
|
||||
|
||||
;; Insert a button detailing the signature status.
|
||||
(notmuch-crypto-insert-sigstatus-button (car (plist-get part :sigstatus))
|
||||
(notmuch-show-get-header :From msg))
|
||||
|
||||
(let ((inner-parts (plist-get part :content))
|
||||
(start (point)))
|
||||
|
@ -668,18 +661,15 @@ will return nil if the CID is unknown or cannot be retrieved."
|
|||
t)
|
||||
|
||||
(defun notmuch-show-insert-part-multipart/encrypted (msg part content-type nth depth button)
|
||||
(button-put button 'face 'notmuch-crypto-part-header)
|
||||
;; add encryption status button if encstatus specified
|
||||
(if (plist-member part :encstatus)
|
||||
(let ((encstatus (car (plist-get part :encstatus))))
|
||||
(notmuch-crypto-insert-encstatus-button encstatus)
|
||||
;; add signature status button if sigstatus specified
|
||||
(if (plist-member part :sigstatus)
|
||||
(let* ((from (notmuch-show-get-header :From msg))
|
||||
(sigstatus (car (plist-get part :sigstatus))))
|
||||
(notmuch-crypto-insert-sigstatus-button sigstatus from))))
|
||||
;; if we're not adding encstatus, tell the user how they can get it
|
||||
(button-put button 'help-echo "Set notmuch-crypto-process-mime to process cryptographic MIME parts."))
|
||||
(when button
|
||||
(button-put button 'face 'notmuch-crypto-part-header))
|
||||
|
||||
;; Insert a button detailing the encryption status.
|
||||
(notmuch-crypto-insert-encstatus-button (car (plist-get part :encstatus)))
|
||||
|
||||
;; Insert a button detailing the signature status.
|
||||
(notmuch-crypto-insert-sigstatus-button (car (plist-get part :sigstatus))
|
||||
(notmuch-show-get-header :From msg))
|
||||
|
||||
(let ((inner-parts (plist-get part :content))
|
||||
(start (point)))
|
||||
|
@ -848,21 +838,16 @@ will return nil if the CID is unknown or cannot be retrieved."
|
|||
;;
|
||||
|
||||
(defun notmuch-show-insert-bodypart-internal (msg part content-type nth depth button)
|
||||
(let ((handlers (notmuch-show-handlers-for content-type)))
|
||||
;; Run the content handlers until one of them returns a non-nil
|
||||
;; value.
|
||||
(while (and handlers
|
||||
(not (condition-case err
|
||||
(funcall (car handlers) msg part content-type nth depth button)
|
||||
;; Specifying `debug' here lets the debugger
|
||||
;; run if `debug-on-error' is non-nil.
|
||||
((debug error)
|
||||
(progn
|
||||
(insert "!!! Bodypart insert error: ")
|
||||
(insert (error-message-string err))
|
||||
(insert " !!!\n") nil)))))
|
||||
(setq handlers (cdr handlers))))
|
||||
t)
|
||||
;; Run the handlers until one of them succeeds.
|
||||
(loop for handler in (notmuch-show-handlers-for content-type)
|
||||
until (condition-case err
|
||||
(funcall handler msg part content-type nth depth button)
|
||||
;; Specifying `debug' here lets the debugger run if
|
||||
;; `debug-on-error' is non-nil.
|
||||
((debug error)
|
||||
(insert "!!! Bodypart handler `" (prin1-to-string handler) "' threw an error:\n"
|
||||
"!!! " (error-message-string err) "\n")
|
||||
nil))))
|
||||
|
||||
(defun notmuch-show-create-part-overlays (button beg end)
|
||||
"Add an overlay to the part between BEG and END"
|
||||
|
@ -929,34 +914,62 @@ will return nil if the CID is unknown or cannot be retrieved."
|
|||
;; showable this returns nil.
|
||||
(notmuch-show-create-part-overlays button part-beg part-end))))
|
||||
|
||||
(defun notmuch-show-mime-type (part)
|
||||
"Return the correct mime-type to use for PART."
|
||||
(let ((content-type (downcase (plist-get part :content-type))))
|
||||
(or (and (string= content-type "application/octet-stream")
|
||||
(notmuch-show-get-mime-type-of-application/octet-stream part))
|
||||
(and (string= content-type "inline patch")
|
||||
"text/x-diff")
|
||||
content-type)))
|
||||
|
||||
;; The following variable can be overridden by let bindings.
|
||||
(defvar notmuch-show-insert-header-p-function 'notmuch-show-insert-header-p
|
||||
"Specify which function decides which part headers get inserted.
|
||||
|
||||
The function should take two parameters, PART and HIDE, and
|
||||
should return non-NIL if a header button should be inserted for
|
||||
this part.")
|
||||
|
||||
(defun notmuch-show-insert-header-p (part hide)
|
||||
;; Show all part buttons except for the first part if it is text/plain.
|
||||
(let ((mime-type (notmuch-show-mime-type part)))
|
||||
(not (and (string= mime-type "text/plain")
|
||||
(<= (plist-get part :id) 1)))))
|
||||
|
||||
(defun notmuch-show-reply-insert-header-p-never (part hide)
|
||||
nil)
|
||||
|
||||
(defun notmuch-show-reply-insert-header-p-trimmed (part hide)
|
||||
(let ((mime-type (notmuch-show-mime-type part)))
|
||||
(and (not (notmuch-match-content-type mime-type "multipart/*"))
|
||||
(not hide))))
|
||||
|
||||
(defun notmuch-show-reply-insert-header-p-minimal (part hide)
|
||||
(let ((mime-type (notmuch-show-mime-type part)))
|
||||
(and (notmuch-match-content-type mime-type "text/*")
|
||||
(not hide))))
|
||||
|
||||
(defun notmuch-show-insert-bodypart (msg part depth &optional hide)
|
||||
"Insert the body part PART at depth DEPTH in the current thread.
|
||||
|
||||
HIDE determines whether to show or hide the part and the button
|
||||
as follows: If HIDE is nil, show the part and the button. If HIDE
|
||||
is t, hide the part initially and show the button. If HIDE is
|
||||
'no-buttons, show the part but do not add any buttons (this is
|
||||
useful for quoting in replies)."
|
||||
is t, hide the part initially and show the button."
|
||||
|
||||
(let* ((content-type (downcase (plist-get part :content-type)))
|
||||
(mime-type (or (and (string= content-type "application/octet-stream")
|
||||
(notmuch-show-get-mime-type-of-application/octet-stream part))
|
||||
(and (string= content-type "inline patch")
|
||||
"text/x-diff")
|
||||
content-type))
|
||||
(mime-type (notmuch-show-mime-type part))
|
||||
(nth (plist-get part :id))
|
||||
(long (and (notmuch-match-content-type mime-type "text/*")
|
||||
(> notmuch-show-max-text-part-size 0)
|
||||
(> (length (plist-get part :content)) notmuch-show-max-text-part-size)))
|
||||
(beg (point))
|
||||
;; We omit the part button for the first (or only) part if
|
||||
;; this is text/plain, or HIDE is 'no-buttons.
|
||||
(button (unless (or (equal hide 'no-buttons)
|
||||
(and (string= mime-type "text/plain") (<= nth 1)))
|
||||
;; This default header-p function omits the part button for
|
||||
;; the first (or only) part if this is text/plain.
|
||||
(button (when (funcall notmuch-show-insert-header-p-function part hide)
|
||||
(notmuch-show-insert-part-header nth mime-type content-type (plist-get part :filename))))
|
||||
;; Hide the part initially if HIDE is t, or if it is too long
|
||||
;; and we have a button to allow toggling (thus reply which
|
||||
;; uses 'no-buttons automatically includes long parts)
|
||||
;; and we have a button to allow toggling.
|
||||
(show-part (not (or (equal hide t)
|
||||
(and long button))))
|
||||
(content-beg (point)))
|
||||
|
@ -966,8 +979,9 @@ useful for quoting in replies)."
|
|||
|
||||
(if show-part
|
||||
(notmuch-show-insert-bodypart-internal msg part mime-type nth depth button)
|
||||
(button-put button :notmuch-lazy-part
|
||||
(list msg part mime-type nth depth button)))
|
||||
(when button
|
||||
(button-put button :notmuch-lazy-part
|
||||
(list msg part mime-type nth depth button))))
|
||||
|
||||
;; Some of the body part handlers leave point somewhere up in the
|
||||
;; part, so we make sure that we're down at the end.
|
||||
|
@ -1199,71 +1213,101 @@ non-nil.
|
|||
The optional BUFFER-NAME provides the name of the buffer in
|
||||
which the message thread is shown. If it is nil (which occurs
|
||||
when the command is called interactively) the argument to the
|
||||
function is used."
|
||||
function is used.
|
||||
|
||||
Returns the buffer containing the messages, or NIL if no messages
|
||||
matched."
|
||||
(interactive "sNotmuch show: \nP")
|
||||
(let ((buffer-name (generate-new-buffer-name
|
||||
(or buffer-name
|
||||
(concat "*notmuch-" thread-id "*")))))
|
||||
(switch-to-buffer (get-buffer-create buffer-name))
|
||||
;; Set the default value for `notmuch-show-process-crypto' in this
|
||||
;; buffer.
|
||||
(setq notmuch-show-process-crypto notmuch-crypto-process-mime)
|
||||
;; Set the default value for
|
||||
;; `notmuch-show-elide-non-matching-messages' in this buffer. If
|
||||
;; elide-toggle is set, invert the default.
|
||||
(setq notmuch-show-elide-non-matching-messages notmuch-show-only-matching-messages)
|
||||
(if elide-toggle
|
||||
(setq notmuch-show-elide-non-matching-messages (not notmuch-show-elide-non-matching-messages)))
|
||||
|
||||
(setq notmuch-show-thread-id thread-id
|
||||
notmuch-show-parent-buffer parent-buffer
|
||||
notmuch-show-query-context query-context)
|
||||
(notmuch-show-build-buffer)
|
||||
(notmuch-show-goto-first-wanted-message)
|
||||
(current-buffer)))
|
||||
|
||||
(defun notmuch-show-build-buffer ()
|
||||
(let ((inhibit-read-only t))
|
||||
;; No need to track undo information for this buffer.
|
||||
(setq buffer-undo-list t)
|
||||
|
||||
(notmuch-show-mode)
|
||||
(add-hook 'post-command-hook #'notmuch-show-command-hook nil t)
|
||||
|
||||
;; Don't track undo information for this buffer
|
||||
(set 'buffer-undo-list t)
|
||||
;; Set various buffer local variables to their appropriate initial
|
||||
;; state. Do this after enabling `notmuch-show-mode' so that they
|
||||
;; aren't wiped out.
|
||||
(setq notmuch-show-thread-id thread-id
|
||||
notmuch-show-parent-buffer parent-buffer
|
||||
notmuch-show-query-context query-context
|
||||
|
||||
notmuch-show-process-crypto notmuch-crypto-process-mime
|
||||
;; If `elide-toggle', invert the default value.
|
||||
notmuch-show-elide-non-matching-messages
|
||||
(if elide-toggle
|
||||
(not notmuch-show-only-matching-messages)
|
||||
notmuch-show-only-matching-messages))
|
||||
|
||||
(add-hook 'post-command-hook #'notmuch-show-command-hook nil t)
|
||||
(jit-lock-register #'notmuch-show-buttonise-links)
|
||||
|
||||
(notmuch-tag-clear-cache)
|
||||
(erase-buffer)
|
||||
(goto-char (point-min))
|
||||
(save-excursion
|
||||
(let* ((basic-args (list notmuch-show-thread-id))
|
||||
(args (if notmuch-show-query-context
|
||||
(append (list "\'") basic-args
|
||||
(list "and (" notmuch-show-query-context ")\'"))
|
||||
(append (list "\'") basic-args (list "\'"))))
|
||||
(cli-args (cons "--exclude=false"
|
||||
(when notmuch-show-elide-non-matching-messages
|
||||
(list "--entire-thread=false")))))
|
||||
|
||||
(notmuch-show-insert-forest (notmuch-query-get-threads (append cli-args args)))
|
||||
;; If the query context reduced the results to nothing, run
|
||||
;; the basic query.
|
||||
(when (and (eq (buffer-size) 0)
|
||||
notmuch-show-query-context)
|
||||
(notmuch-show-insert-forest
|
||||
(notmuch-query-get-threads (append cli-args basic-args)))))
|
||||
(let ((inhibit-read-only t))
|
||||
(if (notmuch-show--build-buffer)
|
||||
;; Messages were inserted into the buffer.
|
||||
(current-buffer)
|
||||
|
||||
(jit-lock-register #'notmuch-show-buttonise-links)
|
||||
;; No messages were inserted - presumably none matched the
|
||||
;; query.
|
||||
(kill-buffer (current-buffer))
|
||||
(ding)
|
||||
(message "No messages matched the query!")
|
||||
nil))))
|
||||
|
||||
(notmuch-show-mapc (lambda () (notmuch-show-set-prop :orig-tags (notmuch-show-get-tags))))
|
||||
(defun notmuch-show--build-buffer (&optional state)
|
||||
"Display messages matching the current buffer context.
|
||||
|
||||
Apply the previously saved STATE if supplied, otherwise show the
|
||||
first relevant message.
|
||||
|
||||
If no messages match the query return NIL."
|
||||
(let* ((basic-args (list notmuch-show-thread-id))
|
||||
(args (if notmuch-show-query-context
|
||||
(append (list "\'") basic-args
|
||||
(list "and (" notmuch-show-query-context ")\'"))
|
||||
(append (list "\'") basic-args (list "\'"))))
|
||||
(cli-args (cons "--exclude=false"
|
||||
(when notmuch-show-elide-non-matching-messages
|
||||
(list "--entire-thread=false"))))
|
||||
|
||||
(forest (or (notmuch-query-get-threads (append cli-args args))
|
||||
;; If a query context reduced the number of
|
||||
;; results to zero, try again without it.
|
||||
(and notmuch-show-query-context
|
||||
(notmuch-query-get-threads (append cli-args basic-args)))))
|
||||
|
||||
;; Must be reset every time we are going to start inserting
|
||||
;; messages into the buffer.
|
||||
(notmuch-show-previous-subject ""))
|
||||
|
||||
(when forest
|
||||
(notmuch-show-insert-forest forest)
|
||||
|
||||
;; Store the original tags for each message so that we can
|
||||
;; display changes.
|
||||
(notmuch-show-mapc
|
||||
(lambda () (notmuch-show-set-prop :orig-tags (notmuch-show-get-tags))))
|
||||
|
||||
;; Set the header line to the subject of the first message.
|
||||
(setq header-line-format
|
||||
(replace-regexp-in-string "%" "%%"
|
||||
(notmuch-sanitize
|
||||
(notmuch-show-strip-re
|
||||
(notmuch-show-get-subject)))))
|
||||
(notmuch-sanitize
|
||||
(notmuch-show-strip-re
|
||||
(notmuch-show-get-subject)))))
|
||||
|
||||
(run-hooks 'notmuch-show-hook))))
|
||||
(run-hooks 'notmuch-show-hook)
|
||||
|
||||
(if state
|
||||
(notmuch-show-apply-state state)
|
||||
;; With no state to apply, just go to the first message.
|
||||
(notmuch-show-goto-first-wanted-message)))
|
||||
|
||||
;; Report back to the caller whether any messages matched.
|
||||
forest))
|
||||
|
||||
(defun notmuch-show-capture-state ()
|
||||
"Capture the state of the current buffer.
|
||||
|
@ -1322,17 +1366,17 @@ reset based on the original query."
|
|||
(let ((inhibit-read-only t)
|
||||
(state (unless reset-state
|
||||
(notmuch-show-capture-state))))
|
||||
;; erase-buffer does not seem to remove overlays, which can lead
|
||||
;; `erase-buffer' does not seem to remove overlays, which can lead
|
||||
;; to weird effects such as remaining images, so remove them
|
||||
;; manually.
|
||||
(remove-overlays)
|
||||
(erase-buffer)
|
||||
(notmuch-show-build-buffer)
|
||||
(if state
|
||||
(notmuch-show-apply-state state)
|
||||
;; We're resetting state, so navigate to the first open message
|
||||
;; and mark it read, just like opening a new show buffer.
|
||||
(notmuch-show-goto-first-wanted-message))))
|
||||
|
||||
(unless (notmuch-show--build-buffer state)
|
||||
;; No messages were inserted.
|
||||
(kill-buffer (current-buffer))
|
||||
(ding)
|
||||
(message "Refreshing the buffer resulted in no messages!"))))
|
||||
|
||||
(defvar notmuch-show-stash-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
|
@ -1373,6 +1417,7 @@ reset based on the original query."
|
|||
(define-key map (kbd "<backtab>") 'notmuch-show-previous-button)
|
||||
(define-key map (kbd "TAB") 'notmuch-show-next-button)
|
||||
(define-key map "f" 'notmuch-show-forward-message)
|
||||
(define-key map "F" 'notmuch-show-forward-open-messages)
|
||||
(define-key map "l" 'notmuch-show-filter-thread)
|
||||
(define-key map "r" 'notmuch-show-reply-sender)
|
||||
(define-key map "R" 'notmuch-show-reply)
|
||||
|
@ -1797,8 +1842,18 @@ any effects from previous calls to
|
|||
(defun notmuch-show-forward-message (&optional prompt-for-sender)
|
||||
"Forward the current message."
|
||||
(interactive "P")
|
||||
(with-current-notmuch-show-message
|
||||
(notmuch-mua-new-forward-message prompt-for-sender)))
|
||||
(notmuch-mua-new-forward-messages (list (notmuch-show-get-message-id))
|
||||
prompt-for-sender))
|
||||
|
||||
(put 'notmuch-show-forward-open-messages 'notmuch-prefix-doc
|
||||
"... and prompt for sender")
|
||||
(defun notmuch-show-forward-open-messages (&optional prompt-for-sender)
|
||||
"Forward the currently open messages."
|
||||
(interactive "P")
|
||||
(let ((open-messages (notmuch-show-get-message-ids-for-open-messages)))
|
||||
(unless open-messages
|
||||
(error "No open messages to forward."))
|
||||
(notmuch-mua-new-forward-messages open-messages prompt-for-sender)))
|
||||
|
||||
(defun notmuch-show-next-message (&optional pop-at-end)
|
||||
"Show the next message.
|
||||
|
@ -1880,12 +1935,15 @@ to show, nil otherwise."
|
|||
"View the original source of the current message."
|
||||
(interactive)
|
||||
(let* ((id (notmuch-show-get-message-id))
|
||||
(buf (get-buffer-create (concat "*notmuch-raw-" id "*"))))
|
||||
(let ((coding-system-for-read 'no-conversion))
|
||||
(call-process notmuch-command nil buf nil "show" "--format=raw" id))
|
||||
(buf (get-buffer-create (concat "*notmuch-raw-" id "*")))
|
||||
(inhibit-read-only t))
|
||||
(switch-to-buffer buf)
|
||||
(erase-buffer)
|
||||
(let ((coding-system-for-read 'no-conversion))
|
||||
(call-process notmuch-command nil t nil "show" "--format=raw" id))
|
||||
(goto-char (point-min))
|
||||
(set-buffer-modified-p nil)
|
||||
(setq buffer-read-only t)
|
||||
(view-buffer buf 'kill-buffer-if-not-modified)))
|
||||
|
||||
(put 'notmuch-show-pipe-message 'notmuch-doc
|
||||
|
@ -2323,3 +2381,5 @@ is destroyed when FN returns."
|
|||
|
||||
|
||||
(provide 'notmuch-show)
|
||||
|
||||
;;; notmuch-show.el ends here
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;; notmuch-tag.el --- tag messages within emacs
|
||||
;;; notmuch-tag.el --- tag messages within emacs
|
||||
;;
|
||||
;; Copyright © Damien Cassou
|
||||
;; Copyright © Carl Worth
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;; notmuch-tree.el --- displaying notmuch forests.
|
||||
;;; notmuch-tree.el --- displaying notmuch forests.
|
||||
;;
|
||||
;; Copyright © Carl Worth
|
||||
;; Copyright © David Edmondson
|
||||
|
@ -22,6 +22,8 @@
|
|||
;; Authors: David Edmondson <dme@dme.org>
|
||||
;; Mark Walters <markwalters1009@gmail.com>
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'mail-parse)
|
||||
|
||||
(require 'notmuch-lib)
|
||||
|
@ -945,3 +947,5 @@ The arguments are:
|
|||
;;
|
||||
|
||||
(provide 'notmuch-tree)
|
||||
|
||||
;;; notmuch-tree.el ends here
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
;;; notmuch-version.el --- Version of notmuch
|
||||
;; -*- emacs-lisp -*-
|
||||
;;
|
||||
;; %AG%
|
||||
|
@ -17,7 +18,11 @@
|
|||
;; You should have received a copy of the GNU General Public License
|
||||
;; along with Notmuch. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(defconst notmuch-emacs-version %VERSION%
|
||||
"Version of Notmuch Emacs MUA.")
|
||||
|
||||
(provide 'notmuch-version)
|
||||
|
||||
;;; notmuch-version.el ends here
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;; notmuch-wash.el --- cleaning up message bodies
|
||||
;;; notmuch-wash.el --- cleaning up message bodies
|
||||
;;
|
||||
;; Copyright © Carl Worth
|
||||
;; Copyright © David Edmondson
|
||||
|
@ -21,6 +21,8 @@
|
|||
;; Authors: Carl Worth <cworth@cworth.org>
|
||||
;; David Edmondson <dme@dme.org>
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'coolj)
|
||||
|
||||
(declare-function notmuch-show-insert-bodypart "notmuch-show" (msg part depth &optional hide))
|
||||
|
@ -423,3 +425,5 @@ for error."
|
|||
;;
|
||||
|
||||
(provide 'notmuch-wash)
|
||||
|
||||
;;; notmuch-wash.el ends here
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;; notmuch.el --- run notmuch within emacs
|
||||
;;; notmuch.el --- run notmuch within emacs
|
||||
;;
|
||||
;; Copyright © Carl Worth
|
||||
;;
|
||||
|
@ -18,6 +18,9 @@
|
|||
;; along with Notmuch. If not, see <http://www.gnu.org/licenses/>.
|
||||
;;
|
||||
;; Authors: Carl Worth <cworth@cworth.org>
|
||||
;; Homepage: https://notmuchmail.org/
|
||||
|
||||
;;; Commentary:
|
||||
|
||||
;; This is an emacs-based interface to the notmuch mail system.
|
||||
;;
|
||||
|
@ -47,6 +50,8 @@
|
|||
;; kudos: Notmuch list <notmuch@notmuchmail.org> (subscription is not
|
||||
;; required, but is available from http://notmuchmail.org).
|
||||
|
||||
;;; Code:
|
||||
|
||||
(eval-when-compile (require 'cl))
|
||||
(require 'mm-view)
|
||||
(require 'message)
|
||||
|
@ -162,7 +167,7 @@ there will be called at other points of notmuch execution."
|
|||
(define-key map "o" 'notmuch-search-toggle-order)
|
||||
(define-key map "c" 'notmuch-search-stash-map)
|
||||
(define-key map "t" 'notmuch-search-filter-by-tag)
|
||||
(define-key map "f" 'notmuch-search-filter)
|
||||
(define-key map "l" 'notmuch-search-filter)
|
||||
(define-key map [mouse-1] 'notmuch-search-show-thread)
|
||||
(define-key map "*" 'notmuch-search-tag-all)
|
||||
(define-key map "a" 'notmuch-search-archive-thread)
|
||||
|
@ -458,7 +463,11 @@ no messages in the region then return nil."
|
|||
(notmuch-search-properties-in-region :subject beg end))
|
||||
|
||||
(defun notmuch-search-show-thread (&optional elide-toggle)
|
||||
"Display the currently selected thread."
|
||||
"Display the currently selected thread.
|
||||
|
||||
With a prefix argument, invert the default value of
|
||||
`notmuch-show-only-matching-messages' when displaying the
|
||||
thread."
|
||||
(interactive "P")
|
||||
(let ((thread-id (notmuch-search-find-thread-id))
|
||||
(subject (notmuch-search-find-subject)))
|
||||
|
@ -988,7 +997,7 @@ Enclose QUERY-STRING in parentheses if it matches
|
|||
query-string))
|
||||
|
||||
(defun notmuch-search-filter (query)
|
||||
"Filter the current search results based on an additional query string.
|
||||
"Filter or LIMIT the current search results based on an additional query string.
|
||||
|
||||
Runs a new search matching only messages that match both the
|
||||
current search results AND the additional query string provided."
|
||||
|
@ -1060,3 +1069,5 @@ notmuch buffers exist, run `notmuch'."
|
|||
(let ((init-file (locate-file notmuch-init-file '("/")
|
||||
(get-load-suffixes))))
|
||||
(if init-file (load init-file nil t t))))
|
||||
|
||||
;;; notmuch.el ends here
|
||||
|
|
|
@ -1635,6 +1635,9 @@ notmuch_database_begin_atomic (notmuch_database_t *notmuch)
|
|||
notmuch->atomic_nesting > 0)
|
||||
goto DONE;
|
||||
|
||||
if (notmuch_database_needs_upgrade(notmuch))
|
||||
return NOTMUCH_STATUS_UPGRADE_REQUIRED;
|
||||
|
||||
try {
|
||||
(static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db))->begin_transaction (false);
|
||||
} catch (const Xapian::Error &error) {
|
||||
|
@ -1758,18 +1761,11 @@ _notmuch_database_split_path (void *ctx,
|
|||
slash = path + strlen (path) - 1;
|
||||
|
||||
/* First, skip trailing slashes. */
|
||||
while (slash != path) {
|
||||
if (*slash != '/')
|
||||
break;
|
||||
|
||||
while (slash != path && *slash == '/')
|
||||
--slash;
|
||||
}
|
||||
|
||||
/* Then, find a slash. */
|
||||
while (slash != path) {
|
||||
if (*slash == '/')
|
||||
break;
|
||||
|
||||
while (slash != path && *slash != '/') {
|
||||
if (basename)
|
||||
*basename = slash;
|
||||
|
||||
|
@ -1777,12 +1773,8 @@ _notmuch_database_split_path (void *ctx,
|
|||
}
|
||||
|
||||
/* Finally, skip multiple slashes. */
|
||||
while (slash != path) {
|
||||
if (*slash != '/')
|
||||
break;
|
||||
|
||||
while (slash != path && *(slash - 1) == '/')
|
||||
--slash;
|
||||
}
|
||||
|
||||
if (slash == path) {
|
||||
if (directory)
|
||||
|
@ -1791,7 +1783,7 @@ _notmuch_database_split_path (void *ctx,
|
|||
*basename = path;
|
||||
} else {
|
||||
if (directory)
|
||||
*directory = talloc_strndup (ctx, path, slash - path + 1);
|
||||
*directory = talloc_strndup (ctx, path, slash - path);
|
||||
}
|
||||
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
|
|
|
@ -377,7 +377,8 @@ _index_mime_part (notmuch_message_t *message,
|
|||
|
||||
disposition = g_mime_object_get_content_disposition (part);
|
||||
if (disposition &&
|
||||
strcmp (disposition->disposition, GMIME_DISPOSITION_ATTACHMENT) == 0)
|
||||
strcasecmp (g_mime_content_disposition_get_disposition (disposition),
|
||||
GMIME_DISPOSITION_ATTACHMENT) == 0)
|
||||
{
|
||||
const char *filename = g_mime_part_get_filename (GMIME_PART (part));
|
||||
|
||||
|
|
115
lib/message.cc
115
lib/message.cc
|
@ -728,7 +728,7 @@ _notmuch_message_add_filename (notmuch_message_t *message,
|
|||
* Note: This function does not remove a document from the database,
|
||||
* even if the specified filename is the only filename for this
|
||||
* message. For that functionality, see
|
||||
* _notmuch_database_remove_message. */
|
||||
* notmuch_database_remove_message. */
|
||||
notmuch_status_t
|
||||
_notmuch_message_remove_filename (notmuch_message_t *message,
|
||||
const char *filename)
|
||||
|
@ -1037,20 +1037,90 @@ _notmuch_message_sync (notmuch_message_t *message)
|
|||
message->modified = FALSE;
|
||||
}
|
||||
|
||||
/* Delete a message document from the database. */
|
||||
/* Delete a message document from the database, leaving a ghost
|
||||
* message in its place */
|
||||
notmuch_status_t
|
||||
_notmuch_message_delete (notmuch_message_t *message)
|
||||
{
|
||||
notmuch_status_t status;
|
||||
Xapian::WritableDatabase *db;
|
||||
const char *mid, *tid, *query_string;
|
||||
notmuch_message_t *ghost;
|
||||
notmuch_private_status_t private_status;
|
||||
notmuch_database_t *notmuch;
|
||||
notmuch_query_t *query;
|
||||
unsigned int count = 0;
|
||||
notmuch_bool_t is_ghost;
|
||||
|
||||
mid = notmuch_message_get_message_id (message);
|
||||
tid = notmuch_message_get_thread_id (message);
|
||||
notmuch = message->notmuch;
|
||||
|
||||
status = _notmuch_database_ensure_writable (message->notmuch);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
db = static_cast <Xapian::WritableDatabase *> (message->notmuch->xapian_db);
|
||||
db = static_cast <Xapian::WritableDatabase *> (notmuch->xapian_db);
|
||||
db->delete_document (message->doc_id);
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
|
||||
/* if this was a ghost to begin with, we are done */
|
||||
private_status = _notmuch_message_has_term (message, "type", "ghost", &is_ghost);
|
||||
if (private_status)
|
||||
return COERCE_STATUS (private_status,
|
||||
"Error trying to determine whether message was a ghost");
|
||||
if (is_ghost)
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
|
||||
query_string = talloc_asprintf (message, "thread:%s", tid);
|
||||
query = notmuch_query_create (notmuch, query_string);
|
||||
if (query == NULL)
|
||||
return NOTMUCH_STATUS_OUT_OF_MEMORY;
|
||||
status = notmuch_query_count_messages_st (query, &count);
|
||||
if (status) {
|
||||
notmuch_query_destroy (query);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
/* reintroduce a ghost in its place because there are still
|
||||
* other active messages in this thread: */
|
||||
ghost = _notmuch_message_create_for_message_id (notmuch, mid, &private_status);
|
||||
if (private_status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND) {
|
||||
private_status = _notmuch_message_initialize_ghost (ghost, tid);
|
||||
if (! private_status)
|
||||
_notmuch_message_sync (ghost);
|
||||
} else if (private_status == NOTMUCH_PRIVATE_STATUS_SUCCESS) {
|
||||
/* this is deeply weird, and we should not have gotten
|
||||
into this state. is there a better error message to
|
||||
return here? */
|
||||
status = NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID;
|
||||
}
|
||||
|
||||
notmuch_message_destroy (ghost);
|
||||
status = COERCE_STATUS (private_status, "Error converting to ghost message");
|
||||
} else {
|
||||
/* the thread is empty; drop all ghost messages from it */
|
||||
notmuch_messages_t *messages;
|
||||
status = _notmuch_query_search_documents (query,
|
||||
"ghost",
|
||||
&messages);
|
||||
if (status == NOTMUCH_STATUS_SUCCESS) {
|
||||
notmuch_status_t last_error = NOTMUCH_STATUS_SUCCESS;
|
||||
while (notmuch_messages_valid (messages)) {
|
||||
message = notmuch_messages_get (messages);
|
||||
status = _notmuch_message_delete (message);
|
||||
if (status) /* we'll report the last failure we see;
|
||||
* if there is more than one failure, we
|
||||
* forget about previous ones */
|
||||
last_error = status;
|
||||
notmuch_message_destroy (message);
|
||||
notmuch_messages_move_to_next (messages);
|
||||
}
|
||||
status = last_error;
|
||||
}
|
||||
}
|
||||
notmuch_query_destroy (query);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Transform a blank message into a ghost message. The caller must
|
||||
|
@ -1180,7 +1250,7 @@ _notmuch_message_remove_term (notmuch_message_t *message,
|
|||
message->doc.remove_term (term);
|
||||
message->modified = TRUE;
|
||||
} catch (const Xapian::InvalidArgumentError) {
|
||||
/* We'll let the philosopher's try to wrestle with the
|
||||
/* We'll let the philosophers try to wrestle with the
|
||||
* question of whether failing to remove that which was not
|
||||
* there in the first place is failure. For us, we'll silently
|
||||
* consider it all good. */
|
||||
|
@ -1193,6 +1263,41 @@ _notmuch_message_remove_term (notmuch_message_t *message,
|
|||
return NOTMUCH_PRIVATE_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
notmuch_private_status_t
|
||||
_notmuch_message_has_term (notmuch_message_t *message,
|
||||
const char *prefix_name,
|
||||
const char *value,
|
||||
notmuch_bool_t *result)
|
||||
{
|
||||
char *term;
|
||||
notmuch_bool_t out = FALSE;
|
||||
notmuch_private_status_t status = NOTMUCH_PRIVATE_STATUS_SUCCESS;
|
||||
|
||||
if (value == NULL)
|
||||
return NOTMUCH_PRIVATE_STATUS_NULL_POINTER;
|
||||
|
||||
term = talloc_asprintf (message, "%s%s",
|
||||
_find_prefix (prefix_name), value);
|
||||
|
||||
if (strlen (term) > NOTMUCH_TERM_MAX)
|
||||
return NOTMUCH_PRIVATE_STATUS_TERM_TOO_LONG;
|
||||
|
||||
try {
|
||||
/* Look for the exact term */
|
||||
Xapian::TermIterator i = message->doc.termlist_begin ();
|
||||
i.skip_to (term);
|
||||
if (i != message->doc.termlist_end () &&
|
||||
!strcmp ((*i).c_str (), term))
|
||||
out = TRUE;
|
||||
} catch (Xapian::Error &error) {
|
||||
status = NOTMUCH_PRIVATE_STATUS_XAPIAN_EXCEPTION;
|
||||
}
|
||||
talloc_free (term);
|
||||
|
||||
*result = out;
|
||||
return status;
|
||||
}
|
||||
|
||||
notmuch_status_t
|
||||
notmuch_message_add_tag (notmuch_message_t *message, const char *tag)
|
||||
{
|
||||
|
|
|
@ -279,6 +279,12 @@ _notmuch_message_remove_term (notmuch_message_t *message,
|
|||
const char *prefix_name,
|
||||
const char *value);
|
||||
|
||||
notmuch_private_status_t
|
||||
_notmuch_message_has_term (notmuch_message_t *message,
|
||||
const char *prefix_name,
|
||||
const char *value,
|
||||
notmuch_bool_t *result);
|
||||
|
||||
notmuch_private_status_t
|
||||
_notmuch_message_gen_terms (notmuch_message_t *message,
|
||||
const char *prefix_name,
|
||||
|
@ -477,6 +483,17 @@ void
|
|||
_notmuch_doc_id_set_remove (notmuch_doc_id_set_t *doc_ids,
|
||||
unsigned int doc_id);
|
||||
|
||||
/* querying xapian documents by type (e.g. "mail" or "ghost"): */
|
||||
notmuch_status_t
|
||||
_notmuch_query_search_documents (notmuch_query_t *query,
|
||||
const char *type,
|
||||
notmuch_messages_t **out);
|
||||
|
||||
notmuch_status_t
|
||||
_notmuch_query_count_documents (notmuch_query_t *query,
|
||||
const char *type,
|
||||
unsigned *count_out);
|
||||
|
||||
/* message.cc */
|
||||
|
||||
void
|
||||
|
|
|
@ -59,8 +59,17 @@ NOTMUCH_BEGIN_DECLS
|
|||
#define LIBNOTMUCH_MINOR_VERSION 3
|
||||
#define LIBNOTMUCH_MICRO_VERSION 0
|
||||
|
||||
|
||||
#if defined (__clang_major__) && __clang_major__ >= 3 \
|
||||
|| defined (__GNUC__) && __GNUC__ >= 5 \
|
||||
|| defined (__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 5
|
||||
#define NOTMUCH_DEPRECATED(major,minor) \
|
||||
__attribute__ ((deprecated ("function deprecated as of libnotmuch " #major "." #minor)))
|
||||
#else
|
||||
#define NOTMUCH_DEPRECATED(major,minor) __attribute__ ((deprecated))
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __DOXYGEN__ */
|
||||
|
||||
/**
|
||||
|
@ -1752,7 +1761,7 @@ notmuch_filenames_t *
|
|||
notmuch_directory_get_child_files (notmuch_directory_t *directory);
|
||||
|
||||
/**
|
||||
* Get a notmuch_filenams_t iterator listing all the filenames of
|
||||
* Get a notmuch_filenames_t iterator listing all the filenames of
|
||||
* sub-directories in the database within the given directory.
|
||||
*
|
||||
* The returned filenames will be the basename-entries only (not
|
||||
|
|
18
lib/query.cc
18
lib/query.cc
|
@ -186,6 +186,14 @@ notmuch_query_search_messages (notmuch_query_t *query)
|
|||
notmuch_status_t
|
||||
notmuch_query_search_messages_st (notmuch_query_t *query,
|
||||
notmuch_messages_t **out)
|
||||
{
|
||||
return _notmuch_query_search_documents (query, "mail", out);
|
||||
}
|
||||
|
||||
notmuch_status_t
|
||||
_notmuch_query_search_documents (notmuch_query_t *query,
|
||||
const char *type,
|
||||
notmuch_messages_t **out)
|
||||
{
|
||||
notmuch_database_t *notmuch = query->notmuch;
|
||||
const char *query_string = query->query_string;
|
||||
|
@ -208,7 +216,7 @@ notmuch_query_search_messages_st (notmuch_query_t *query,
|
|||
Xapian::Enquire enquire (*notmuch->xapian_db);
|
||||
Xapian::Query mail_query (talloc_asprintf (query, "%s%s",
|
||||
_find_prefix ("type"),
|
||||
"mail"));
|
||||
type));
|
||||
Xapian::Query string_query, final_query, exclude_query;
|
||||
Xapian::MSet mset;
|
||||
Xapian::MSetIterator iterator;
|
||||
|
@ -553,6 +561,12 @@ notmuch_query_count_messages (notmuch_query_t *query)
|
|||
|
||||
notmuch_status_t
|
||||
notmuch_query_count_messages_st (notmuch_query_t *query, unsigned *count_out)
|
||||
{
|
||||
return _notmuch_query_count_documents (query, "mail", count_out);
|
||||
}
|
||||
|
||||
notmuch_status_t
|
||||
_notmuch_query_count_documents (notmuch_query_t *query, const char *type, unsigned *count_out)
|
||||
{
|
||||
notmuch_database_t *notmuch = query->notmuch;
|
||||
const char *query_string = query->query_string;
|
||||
|
@ -562,7 +576,7 @@ notmuch_query_count_messages_st (notmuch_query_t *query, unsigned *count_out)
|
|||
Xapian::Enquire enquire (*notmuch->xapian_db);
|
||||
Xapian::Query mail_query (talloc_asprintf (query, "%s%s",
|
||||
_find_prefix ("type"),
|
||||
"mail"));
|
||||
type));
|
||||
Xapian::Query string_query, final_query, exclude_query;
|
||||
Xapian::MSet mset;
|
||||
unsigned int flags = (Xapian::QueryParser::FLAG_BOOLEAN |
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include <gmime/gmime.h>
|
||||
|
||||
typedef GMimeCryptoContext notmuch_crypto_context_t;
|
||||
/* This is automatically included only since gmime 2.6.10 */
|
||||
#include <gmime/gmime-pkcs7-context.h>
|
||||
|
||||
#include "notmuch.h"
|
||||
|
||||
|
@ -70,6 +72,7 @@ typedef struct notmuch_show_format {
|
|||
|
||||
typedef struct notmuch_crypto {
|
||||
notmuch_crypto_context_t* gpgctx;
|
||||
notmuch_crypto_context_t* pkcs7ctx;
|
||||
notmuch_bool_t verify;
|
||||
notmuch_bool_t decrypt;
|
||||
const char *gpgpath;
|
||||
|
@ -407,8 +410,8 @@ struct mime_node {
|
|||
/* Construct a new MIME node pointing to the root message part of
|
||||
* message. If crypto->verify is true, signed child parts will be
|
||||
* verified. If crypto->decrypt is true, encrypted child parts will be
|
||||
* decrypted. If crypto->gpgctx is NULL, it will be lazily
|
||||
* initialized.
|
||||
* decrypted. If the crypto contexts (crypto->gpgctx or
|
||||
* crypto->pkcs7) are NULL, they will be lazily initialized.
|
||||
*
|
||||
* Return value:
|
||||
*
|
||||
|
@ -459,6 +462,11 @@ print_status_query (const char *loc,
|
|||
const notmuch_query_t *query,
|
||||
notmuch_status_t status);
|
||||
|
||||
notmuch_status_t
|
||||
print_status_database (const char *loc,
|
||||
const notmuch_database_t *database,
|
||||
notmuch_status_t status);
|
||||
|
||||
#include "command-line-arguments.h"
|
||||
|
||||
extern char *notmuch_requested_db_uuid;
|
||||
|
|
|
@ -30,8 +30,8 @@ escape ()
|
|||
printf -v $2 '%s' "${__escape_arg__//\"/\\\"}"
|
||||
}
|
||||
|
||||
EMACS=${EMACS-emacs}
|
||||
EMACSCLIENT=${EMACSCLIENT-emacsclient}
|
||||
EMACS=${EMACS:-emacs}
|
||||
EMACSCLIENT=${EMACSCLIENT:-emacsclient}
|
||||
|
||||
PRINT_ONLY=
|
||||
NO_WINDOW=
|
||||
|
|
|
@ -281,6 +281,10 @@ add_file (notmuch_database_t *notmuch, const char *filename,
|
|||
fprintf (stderr, "Note: Ignoring non-mail file: %s\n", filename);
|
||||
break;
|
||||
/* Fatal issues. Don't process anymore. */
|
||||
case NOTMUCH_STATUS_FILE_ERROR:
|
||||
fprintf (stderr, "Unexpected error with file %s\n", filename);
|
||||
(void) print_status_database ("add_file", notmuch, status);
|
||||
goto DONE;
|
||||
case NOTMUCH_STATUS_READ_ONLY_DATABASE:
|
||||
case NOTMUCH_STATUS_XAPIAN_EXCEPTION:
|
||||
case NOTMUCH_STATUS_OUT_OF_MEMORY:
|
||||
|
@ -445,7 +449,7 @@ add_files (notmuch_database_t *notmuch,
|
|||
*/
|
||||
if (_entry_in_ignore_list (entry->d_name, state)) {
|
||||
if (state->debug)
|
||||
printf ("(D) add_files_recursive, pass 1: explicitly ignoring %s/%s\n",
|
||||
printf ("(D) add_files, pass 1: explicitly ignoring %s/%s\n",
|
||||
path, entry->d_name);
|
||||
continue;
|
||||
}
|
||||
|
@ -513,9 +517,8 @@ add_files (notmuch_database_t *notmuch,
|
|||
/* Ignore files & directories user has configured to be ignored */
|
||||
if (_entry_in_ignore_list (entry->d_name, state)) {
|
||||
if (state->debug)
|
||||
printf ("(D) add_files_recursive, pass 2: explicitly ignoring %s/%s\n",
|
||||
path,
|
||||
entry->d_name);
|
||||
printf ("(D) add_files, pass 2: explicitly ignoring %s/%s\n",
|
||||
path, entry->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -529,7 +532,7 @@ add_files (notmuch_database_t *notmuch,
|
|||
notmuch_filenames_get (db_files));
|
||||
|
||||
if (state->debug)
|
||||
printf ("(D) add_files_recursive, pass 2: queuing passed file %s for deletion from database\n",
|
||||
printf ("(D) add_files, pass 2: queuing passed file %s for deletion from database\n",
|
||||
absolute);
|
||||
|
||||
_filename_list_add (state->removed_files, absolute);
|
||||
|
@ -547,7 +550,7 @@ add_files (notmuch_database_t *notmuch,
|
|||
char *absolute = talloc_asprintf (state->removed_directories,
|
||||
"%s/%s", path, filename);
|
||||
if (state->debug)
|
||||
printf ("(D) add_files_recursive, pass 2: queuing passed directory %s for deletion from database\n",
|
||||
printf ("(D) add_files, pass 2: queuing passed directory %s for deletion from database\n",
|
||||
absolute);
|
||||
|
||||
_filename_list_add (state->removed_directories, absolute);
|
||||
|
@ -618,7 +621,7 @@ add_files (notmuch_database_t *notmuch,
|
|||
"%s/%s", path,
|
||||
notmuch_filenames_get (db_files));
|
||||
if (state->debug)
|
||||
printf ("(D) add_files_recursive, pass 3: queuing leftover file %s for deletion from database\n",
|
||||
printf ("(D) add_files, pass 3: queuing leftover file %s for deletion from database\n",
|
||||
absolute);
|
||||
|
||||
_filename_list_add (state->removed_files, absolute);
|
||||
|
@ -633,7 +636,7 @@ add_files (notmuch_database_t *notmuch,
|
|||
notmuch_filenames_get (db_subdirs));
|
||||
|
||||
if (state->debug)
|
||||
printf ("(D) add_files_recursive, pass 3: queuing leftover directory %s for deletion from database\n",
|
||||
printf ("(D) add_files, pass 3: queuing leftover directory %s for deletion from database\n",
|
||||
absolute);
|
||||
|
||||
_filename_list_add (state->removed_directories, absolute);
|
||||
|
|
|
@ -80,7 +80,8 @@ format_part_reply (mime_node_t *node)
|
|||
show_text_part_content (node->part, stream_stdout, NOTMUCH_SHOW_TEXT_PART_REPLY);
|
||||
g_object_unref(stream_stdout);
|
||||
} else if (disposition &&
|
||||
strcmp (disposition->disposition, GMIME_DISPOSITION_ATTACHMENT) == 0) {
|
||||
strcasecmp (g_mime_content_disposition_get_disposition (disposition),
|
||||
GMIME_DISPOSITION_ATTACHMENT) == 0) {
|
||||
const char *filename = g_mime_part_get_filename (GMIME_PART (node->part));
|
||||
printf ("Attachment: %s (%s)\n", filename,
|
||||
g_mime_content_type_to_string (content_type));
|
||||
|
@ -331,7 +332,7 @@ add_recipients_from_message (GMimeMessage *reply,
|
|||
* field and use the From header. This ensures the original sender
|
||||
* will get the reply even if not subscribed to the list. Note
|
||||
* that the address in the Reply-To header will always appear in
|
||||
* the reply.
|
||||
* the reply if reply_all is true.
|
||||
*/
|
||||
if (reply_to_header_is_redundant (message)) {
|
||||
reply_to_map[0].header = "from";
|
||||
|
|
|
@ -456,7 +456,8 @@ format_part_text (const void *ctx, sprinter_t *sp, mime_node_t *node,
|
|||
g_mime_part_get_filename (GMIME_PART (node->part)) : NULL;
|
||||
|
||||
if (disposition &&
|
||||
strcmp (disposition->disposition, GMIME_DISPOSITION_ATTACHMENT) == 0)
|
||||
strcasecmp (g_mime_content_disposition_get_disposition (disposition),
|
||||
GMIME_DISPOSITION_ATTACHMENT) == 0)
|
||||
part_type = "attachment";
|
||||
else
|
||||
part_type = "part";
|
||||
|
|
|
@ -237,10 +237,6 @@ notmuch_tag_command (notmuch_config_t *config, int argc, char *argv[])
|
|||
fprintf (stderr, "Can't specify both cmdline and stdin!\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (remove_all) {
|
||||
fprintf (stderr, "Can't specify both --remove-all and --batch\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
} else {
|
||||
tag_ops = tag_op_list_create (config);
|
||||
if (tag_ops == NULL) {
|
||||
|
|
17
status.c
17
status.c
|
@ -19,3 +19,20 @@ print_status_query (const char *loc,
|
|||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
notmuch_status_t
|
||||
print_status_database (const char *loc,
|
||||
const notmuch_database_t *notmuch,
|
||||
notmuch_status_t status)
|
||||
{
|
||||
if (status) {
|
||||
const char *msg;
|
||||
|
||||
fprintf (stderr, "%s: %s\n", loc,
|
||||
notmuch_status_to_string (status));
|
||||
msg = notmuch_database_status_string (notmuch);
|
||||
if (msg)
|
||||
fputs (msg, stderr);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
|
1
test/.gitignore
vendored
1
test/.gitignore
vendored
|
@ -7,4 +7,5 @@ smtp-dummy
|
|||
symbol-test
|
||||
make-db-version
|
||||
test-results
|
||||
ghost-report
|
||||
tmp.*
|
||||
|
|
|
@ -38,6 +38,9 @@ $(dir)/parse-time: $(dir)/parse-time.o parse-time-string/parse-time-string.o
|
|||
$(dir)/make-db-version: $(dir)/make-db-version.o
|
||||
$(call quiet,CXX) $^ -o $@ $(LDFLAGS) $(XAPIAN_LDFLAGS)
|
||||
|
||||
$(dir)/ghost-report: $(dir)/ghost-report.o
|
||||
$(call quiet,CXX) $^ -o $@ $(LDFLAGS) $(XAPIAN_LDFLAGS)
|
||||
|
||||
.PHONY: test check
|
||||
|
||||
test_main_srcs=$(dir)/arg-test.c \
|
||||
|
@ -47,6 +50,7 @@ test_main_srcs=$(dir)/arg-test.c \
|
|||
$(dir)/smtp-dummy.c \
|
||||
$(dir)/symbol-test.cc \
|
||||
$(dir)/make-db-version.cc \
|
||||
$(dir)/ghost-report.cc
|
||||
|
||||
test_srcs=$(test_main_srcs) $(dir)/database-test.c
|
||||
|
||||
|
@ -57,7 +61,7 @@ test-binaries: $(TEST_BINARIES)
|
|||
|
||||
test: all test-binaries
|
||||
ifeq ($V,)
|
||||
@echo 'Use "$(MAKE) V=1" to print test headings and PASSIng results.'
|
||||
@echo 'Use "$(MAKE) V=1" to print test headings and PASSing results.'
|
||||
@env NOTMUCH_TEST_QUIET=1 ${test_src_dir}/notmuch-test $(OPTIONS)
|
||||
else
|
||||
# The user has explicitly enabled quiet execution.
|
||||
|
|
34
test/README
34
test/README
|
@ -8,10 +8,17 @@ enhance.
|
|||
|
||||
Prerequisites
|
||||
-------------
|
||||
The test system itself requires:
|
||||
|
||||
- bash(1) version 4.0 or newer
|
||||
|
||||
Without bash 4.0+ the tests just refuse to run.
|
||||
|
||||
Some tests require external dependencies to run. Without them, they
|
||||
will be skipped, or (rarely) marked failed. Please install these, so
|
||||
that you know if you break anything.
|
||||
|
||||
- GNU tar(1)
|
||||
- dtach(1)
|
||||
- emacs(1)
|
||||
- emacsclient(1)
|
||||
|
@ -19,14 +26,21 @@ that you know if you break anything.
|
|||
- gpg(1)
|
||||
- python(1)
|
||||
|
||||
If your system lacks these tools or have older, non-upgreable versions
|
||||
of these, please (possibly compile and) install these to some other
|
||||
path, for example /usr/local/bin or /opt/gnu/bin. Then prepend the
|
||||
chosen directory to your PATH before running the tests.
|
||||
|
||||
e.g. env PATH=/opt/gnu/bin:$PATH make test
|
||||
|
||||
Running Tests
|
||||
-------------
|
||||
The easiest way to run tests is to say "make test", (or simply run the
|
||||
notmuch-test script). Either command will run all available tests.
|
||||
|
||||
Alternately, you can run a specific subset of tests by simply invoking
|
||||
one of the executable scripts in this directory, (such as ./search,
|
||||
./reply, etc). Note that you will probably want "make test-binaries"
|
||||
one of the executable scripts in this directory, (such as ./T*-search.sh,
|
||||
./T*-reply.sh, etc). Note that you will probably want "make test-binaries"
|
||||
before running individual tests.
|
||||
|
||||
The following command-line options are available when running tests:
|
||||
|
@ -80,9 +94,9 @@ can be specified as follows:
|
|||
You can choose an emacs binary (and corresponding emacsclient) to run
|
||||
the tests in one of the following ways.
|
||||
|
||||
TEST_EMACS=my-special-emacs TEST_EMACSCLIENT=my-emacsclient make test
|
||||
TEST_EMACS=my-special-emacs TEST_EMACSCLIENT=my-emacsclient ./emacs
|
||||
make test TEST_EMACS=my-special-emacs TEST_EMACSCLIENT=my-emacsclient
|
||||
TEST_EMACS=my-emacs TEST_EMACSCLIENT=my-emacsclient make test
|
||||
TEST_EMACS=my-emacs TEST_EMACSCLIENT=my-emacsclient ./T*-emacs.sh
|
||||
make test TEST_EMACS=my-emacs TEST_EMACSCLIENT=my-emacsclient
|
||||
|
||||
Some tests may require a c compiler. You can choose the name and flags similarly
|
||||
to with emacs, e.g.
|
||||
|
@ -126,9 +140,13 @@ skipped by the user, as failures.
|
|||
|
||||
Writing Tests
|
||||
-------------
|
||||
The test script is written as a shell script. It should start with
|
||||
the standard "#!/usr/bin/env bash" with copyright notices, and an
|
||||
assignment to variable 'test_description', like this:
|
||||
The test script is written as a shell script. It is to be named as
|
||||
Tddd-testname.sh where 'ddd' is three digits and 'testname' the "bare"
|
||||
name of your test. Tests will be run in order the 'ddd' part determines.
|
||||
|
||||
The test script should start with the standard "#!/usr/bin/env bash"
|
||||
with copyright notices, and an assignment to variable 'test_description',
|
||||
like this:
|
||||
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
|
|
|
@ -64,7 +64,7 @@ generate_message
|
|||
notmuch new > /dev/null
|
||||
mv "$gen_msg_filename" "${gen_msg_filename}"-renamed
|
||||
output=$(NOTMUCH_NEW --debug)
|
||||
test_expect_equal "$output" "(D) add_files_recursive, pass 2: queuing passed file ${gen_msg_filename} for deletion from database
|
||||
test_expect_equal "$output" "(D) add_files, pass 2: queuing passed file ${gen_msg_filename} for deletion from database
|
||||
No new mail. Detected 1 file rename."
|
||||
|
||||
|
||||
|
@ -72,7 +72,7 @@ test_begin_subtest "Deleted message"
|
|||
|
||||
rm "${gen_msg_filename}"-renamed
|
||||
output=$(NOTMUCH_NEW --debug)
|
||||
test_expect_equal "$output" "(D) add_files_recursive, pass 3: queuing leftover file ${gen_msg_filename}-renamed for deletion from database
|
||||
test_expect_equal "$output" "(D) add_files, pass 3: queuing leftover file ${gen_msg_filename}-renamed for deletion from database
|
||||
No new mail. Removed 1 message."
|
||||
|
||||
|
||||
|
@ -88,7 +88,7 @@ notmuch new > /dev/null
|
|||
mv "${MAIL_DIR}"/dir "${MAIL_DIR}"/dir-renamed
|
||||
|
||||
output=$(NOTMUCH_NEW --debug)
|
||||
test_expect_equal "$output" "(D) add_files_recursive, pass 2: queuing passed directory ${MAIL_DIR}/dir for deletion from database
|
||||
test_expect_equal "$output" "(D) add_files, pass 2: queuing passed directory ${MAIL_DIR}/dir for deletion from database
|
||||
No new mail. Detected 3 file renames."
|
||||
|
||||
|
||||
|
@ -96,7 +96,7 @@ test_begin_subtest "Deleted directory"
|
|||
rm -rf "${MAIL_DIR}"/dir-renamed
|
||||
|
||||
output=$(NOTMUCH_NEW --debug)
|
||||
test_expect_equal "$output" "(D) add_files_recursive, pass 2: queuing passed directory ${MAIL_DIR}/dir-renamed for deletion from database
|
||||
test_expect_equal "$output" "(D) add_files, pass 2: queuing passed directory ${MAIL_DIR}/dir-renamed for deletion from database
|
||||
No new mail. Removed 3 messages."
|
||||
|
||||
|
||||
|
@ -115,7 +115,7 @@ test_begin_subtest "Deleted directory (end of list)"
|
|||
rm -rf "${MAIL_DIR}"/zzz
|
||||
|
||||
output=$(NOTMUCH_NEW --debug)
|
||||
test_expect_equal "$output" "(D) add_files_recursive, pass 3: queuing leftover directory ${MAIL_DIR}/zzz for deletion from database
|
||||
test_expect_equal "$output" "(D) add_files, pass 3: queuing leftover directory ${MAIL_DIR}/zzz for deletion from database
|
||||
No new mail. Removed 3 messages."
|
||||
|
||||
|
||||
|
@ -166,9 +166,18 @@ test_begin_subtest "Deleted two-level directory"
|
|||
rm -rf "${MAIL_DIR}"/two
|
||||
|
||||
output=$(NOTMUCH_NEW --debug)
|
||||
test_expect_equal "$output" "(D) add_files_recursive, pass 3: queuing leftover directory ${MAIL_DIR}/two for deletion from database
|
||||
test_expect_equal "$output" "(D) add_files, pass 3: queuing leftover directory ${MAIL_DIR}/two for deletion from database
|
||||
No new mail. Removed 3 messages."
|
||||
|
||||
test_begin_subtest "One character directory at top level"
|
||||
|
||||
generate_message [dir]=A
|
||||
generate_message [dir]=A/B
|
||||
generate_message [dir]=A/B/C
|
||||
|
||||
output=$(NOTMUCH_NEW --debug)
|
||||
test_expect_equal "$output" "Added 3 new messages to the database."
|
||||
|
||||
test_begin_subtest "Support single-message mbox"
|
||||
cat > "${MAIL_DIR}"/mbox_file1 <<EOF
|
||||
From test_suite@notmuchmail.org Fri Jan 5 15:43:57 2001
|
||||
|
@ -227,20 +236,20 @@ mkdir -p "${MAIL_DIR}"/one/two/three/.git
|
|||
touch "${MAIL_DIR}"/{one,one/two,one/two/three}/ignored_file
|
||||
output=$(NOTMUCH_NEW --debug 2>&1 | sort)
|
||||
test_expect_equal "$output" \
|
||||
"(D) add_files_recursive, pass 1: explicitly ignoring ${MAIL_DIR}/.git
|
||||
(D) add_files_recursive, pass 1: explicitly ignoring ${MAIL_DIR}/.ignored_hidden_file
|
||||
(D) add_files_recursive, pass 1: explicitly ignoring ${MAIL_DIR}/ignored_file
|
||||
(D) add_files_recursive, pass 1: explicitly ignoring ${MAIL_DIR}/one/ignored_file
|
||||
(D) add_files_recursive, pass 1: explicitly ignoring ${MAIL_DIR}/one/two/ignored_file
|
||||
(D) add_files_recursive, pass 1: explicitly ignoring ${MAIL_DIR}/one/two/three/.git
|
||||
(D) add_files_recursive, pass 1: explicitly ignoring ${MAIL_DIR}/one/two/three/ignored_file
|
||||
(D) add_files_recursive, pass 2: explicitly ignoring ${MAIL_DIR}/.git
|
||||
(D) add_files_recursive, pass 2: explicitly ignoring ${MAIL_DIR}/.ignored_hidden_file
|
||||
(D) add_files_recursive, pass 2: explicitly ignoring ${MAIL_DIR}/ignored_file
|
||||
(D) add_files_recursive, pass 2: explicitly ignoring ${MAIL_DIR}/one/ignored_file
|
||||
(D) add_files_recursive, pass 2: explicitly ignoring ${MAIL_DIR}/one/two/ignored_file
|
||||
(D) add_files_recursive, pass 2: explicitly ignoring ${MAIL_DIR}/one/two/three/.git
|
||||
(D) add_files_recursive, pass 2: explicitly ignoring ${MAIL_DIR}/one/two/three/ignored_file
|
||||
"(D) add_files, pass 1: explicitly ignoring ${MAIL_DIR}/.git
|
||||
(D) add_files, pass 1: explicitly ignoring ${MAIL_DIR}/.ignored_hidden_file
|
||||
(D) add_files, pass 1: explicitly ignoring ${MAIL_DIR}/ignored_file
|
||||
(D) add_files, pass 1: explicitly ignoring ${MAIL_DIR}/one/ignored_file
|
||||
(D) add_files, pass 1: explicitly ignoring ${MAIL_DIR}/one/two/ignored_file
|
||||
(D) add_files, pass 1: explicitly ignoring ${MAIL_DIR}/one/two/three/.git
|
||||
(D) add_files, pass 1: explicitly ignoring ${MAIL_DIR}/one/two/three/ignored_file
|
||||
(D) add_files, pass 2: explicitly ignoring ${MAIL_DIR}/.git
|
||||
(D) add_files, pass 2: explicitly ignoring ${MAIL_DIR}/.ignored_hidden_file
|
||||
(D) add_files, pass 2: explicitly ignoring ${MAIL_DIR}/ignored_file
|
||||
(D) add_files, pass 2: explicitly ignoring ${MAIL_DIR}/one/ignored_file
|
||||
(D) add_files, pass 2: explicitly ignoring ${MAIL_DIR}/one/two/ignored_file
|
||||
(D) add_files, pass 2: explicitly ignoring ${MAIL_DIR}/one/two/three/.git
|
||||
(D) add_files, pass 2: explicitly ignoring ${MAIL_DIR}/one/two/three/ignored_file
|
||||
No new mail."
|
||||
|
||||
|
||||
|
@ -284,9 +293,9 @@ notmuch config set new.tags $OLDCONFIG
|
|||
|
||||
|
||||
test_begin_subtest "Xapian exception: read only files"
|
||||
chmod u-w ${MAIL_DIR}/.notmuch/xapian/*.DB
|
||||
chmod u-w ${MAIL_DIR}/.notmuch/xapian/*.${db_ending}
|
||||
output=$(NOTMUCH_NEW --debug 2>&1 | sed 's/: .*$//' )
|
||||
chmod u+w ${MAIL_DIR}/.notmuch/xapian/*.DB
|
||||
chmod u+w ${MAIL_DIR}/.notmuch/xapian/*.${db_ending}
|
||||
test_expect_equal "$output" "A Xapian exception occurred opening database"
|
||||
|
||||
test_done
|
||||
|
|
|
@ -95,7 +95,7 @@ test_expect_equal_file EXPECTED OUTPUT
|
|||
|
||||
backup_database
|
||||
test_begin_subtest "error message for database open"
|
||||
dd if=/dev/zero of="${MAIL_DIR}/.notmuch/xapian/postlist.DB" count=3
|
||||
dd if=/dev/zero of="${MAIL_DIR}/.notmuch/xapian/postlist.${db_ending}" count=3
|
||||
notmuch count '*' 2>OUTPUT 1>/dev/null
|
||||
output=$(sed 's/^\(A Xapian exception [^:]*\):.*$/\1/' OUTPUT)
|
||||
test_expect_equal "${output}" "A Xapian exception occurred opening database"
|
||||
|
@ -105,7 +105,7 @@ cat <<EOF > count-files.gdb
|
|||
set breakpoint pending on
|
||||
break count_files
|
||||
commands
|
||||
shell cp /dev/null ${MAIL_DIR}/.notmuch/xapian/postlist.DB
|
||||
shell cp /dev/null ${MAIL_DIR}/.notmuch/xapian/postlist.${db_ending}
|
||||
continue
|
||||
end
|
||||
run
|
||||
|
|
|
@ -38,6 +38,17 @@ test_expect_equal "$output" "\
|
|||
thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; One ()
|
||||
thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Two (tag5 tag6 unread)"
|
||||
|
||||
test_begin_subtest "Remove all with batch"
|
||||
notmuch tag +tag1 One
|
||||
notmuch tag --remove-all --batch <<EOF
|
||||
-- One
|
||||
+tag3 +tag4 +inbox -- Two
|
||||
EOF
|
||||
output=$(notmuch search \* | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "\
|
||||
thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; One ()
|
||||
thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag3 tag4)"
|
||||
|
||||
test_begin_subtest "Remove all with a no-op"
|
||||
notmuch tag +inbox +tag1 +unread One
|
||||
notmuch tag --remove-all +foo +inbox +tag1 -foo +unread Two
|
||||
|
@ -102,6 +113,20 @@ notmuch search \* | notmuch_search_sanitize > OUTPUT
|
|||
notmuch restore --format=batch-tag < backup.tags
|
||||
test_expect_equal_file batch.expected OUTPUT
|
||||
|
||||
test_begin_subtest "--batch --input --remove-all"
|
||||
notmuch dump --format=batch-tag > backup.tags
|
||||
notmuch tag +foo +bar -- One
|
||||
notmuch tag +tag7 -- Two
|
||||
notmuch tag --batch --input=batch.in --remove-all
|
||||
notmuch search \* | notmuch_search_sanitize > OUTPUT
|
||||
notmuch restore --format=batch-tag < backup.tags
|
||||
cat > batch_removeall.expected <<EOF
|
||||
thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; One (@ tag6)
|
||||
thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Two (tag6)
|
||||
EOF
|
||||
test_expect_equal_file batch_removeall.expected OUTPUT
|
||||
rm batch_removeall.expected
|
||||
|
||||
test_begin_subtest "--batch, blank lines and comments"
|
||||
notmuch dump | sort > EXPECTED
|
||||
notmuch tag --batch <<EOF
|
||||
|
@ -262,9 +287,9 @@ test_expect_code 1 "Empty tag names" 'notmuch tag + One'
|
|||
test_expect_code 1 "Tag name beginning with -" 'notmuch tag +- One'
|
||||
|
||||
test_begin_subtest "Xapian exception: read only files"
|
||||
chmod u-w ${MAIL_DIR}/.notmuch/xapian/*.DB
|
||||
chmod u-w ${MAIL_DIR}/.notmuch/xapian/*.${db_ending}
|
||||
output=$(notmuch tag +something '*' 2>&1 | sed 's/: .*$//' )
|
||||
chmod u+w ${MAIL_DIR}/.notmuch/xapian/*.DB
|
||||
chmod u+w ${MAIL_DIR}/.notmuch/xapian/*.${db_ending}
|
||||
test_expect_equal "$output" "A Xapian exception occurred opening database"
|
||||
|
||||
test_done
|
||||
|
|
|
@ -763,4 +763,56 @@ test_begin_subtest "indexes mime-type #3"
|
|||
output=$(notmuch search from:todd and mimetype:multipart/alternative | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2014-01-12 [1/1] Todd; odd content types (inbox unread)"
|
||||
|
||||
test_begin_subtest "case of Content-Disposition doesn't matter for indexing"
|
||||
cat <<EOF > ${MAIL_DIR}/content-disposition
|
||||
Return-path: <david@tethera.net>
|
||||
Envelope-to: david@tethera.net
|
||||
Delivery-date: Sun, 04 Oct 2015 09:16:03 -0300
|
||||
Received: from gitolite.debian.net ([87.98.215.224])
|
||||
by yantan.tethera.net with esmtps (TLS1.2:DHE_RSA_AES_128_CBC_SHA1:128)
|
||||
(Exim 4.80)
|
||||
(envelope-from <david@tethera.net>)
|
||||
id 1ZiiCx-0007iz-RK
|
||||
for david@tethera.net; Sun, 04 Oct 2015 09:16:03 -0300
|
||||
Received: from remotemail by gitolite.debian.net with local (Exim 4.80)
|
||||
(envelope-from <david@tethera.net>)
|
||||
id 1ZiiC8-0002Rz-Uf; Sun, 04 Oct 2015 12:15:12 +0000
|
||||
Received: (nullmailer pid 28621 invoked by uid 1000); Sun, 04 Oct 2015
|
||||
12:14:53 -0000
|
||||
From: David Bremner <david@tethera.net>
|
||||
To: David Bremner <david@tethera.net>
|
||||
Subject: test attachment
|
||||
User-Agent: Notmuch/0.20.2+93~g33c8777 (http://notmuchmail.org) Emacs/24.5.1
|
||||
(x86_64-pc-linux-gnu)
|
||||
Date: Sun, 04 Oct 2015 09:14:53 -0300
|
||||
Message-ID: <87io6m96f6.fsf@zancas.localnet>
|
||||
MIME-Version: 1.0
|
||||
Content-Type: multipart/mixed; boundary="=-=-="
|
||||
|
||||
--=-=-=
|
||||
Content-Type: text/plain
|
||||
Content-Disposition: ATTACHMENT; filename=hello.txt
|
||||
Content-Description: this is a very exciting file
|
||||
|
||||
hello
|
||||
|
||||
--=-=-=
|
||||
Content-Type: text/plain
|
||||
|
||||
|
||||
world
|
||||
|
||||
--=-=-=--
|
||||
|
||||
EOF
|
||||
NOTMUCH_NEW
|
||||
|
||||
cat <<EOF > EXPECTED
|
||||
attachment
|
||||
inbox
|
||||
unread
|
||||
EOF
|
||||
|
||||
notmuch search --output=tags id:87io6m96f6.fsf@zancas.localnet > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
test_done
|
||||
|
|
|
@ -130,7 +130,7 @@ test_expect_equal "$output" "thread:XXX 2009-11-18 [4/4] Jjgod Jiang, Alexande
|
|||
test_begin_subtest "Add tag (large query)"
|
||||
# We use a long query to force us into batch mode and use a funny tag
|
||||
# that requires escaping for batch tagging.
|
||||
test_emacs "(notmuch-tag (concat \"$os_x_darwin_thread\" \" or \" (make-string notmuch-tag-argument-limit ?x)) (list \"+tag-from-%-large-query\"))"
|
||||
test_emacs "(notmuch-tag (concat \"$os_x_darwin_thread\" \" or \" (mapconcat #'identity (make-list notmuch-tag-argument-limit \"x\") \"-\")) (list \"+tag-from-%-large-query\"))"
|
||||
output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag-from-%-large-query unread)"
|
||||
notmuch tag -tag-from-%-large-query $os_x_darwin_thread
|
||||
|
@ -394,6 +394,8 @@ User-Agent: Notmuch/XXX Emacs/XXX
|
|||
--text follows this line--
|
||||
Adrian Perez de Castro <aperez@igalia.com> writes:
|
||||
|
||||
> [ Unknown signature status ]
|
||||
>
|
||||
> Hello to all,
|
||||
>
|
||||
> I have just heard about Not Much today in some random Linux-related news
|
||||
|
@ -473,6 +475,38 @@ Alex Botero-Lowry <alex.boterolowry@gmail.com> writes:
|
|||
> and http://mail-index.netbsd.org/pkgsrc-bugs/2006/06/07/msg016808.htmlspecifically
|
||||
> uses 64 as the
|
||||
> buffer size.
|
||||
> From e3bc4bbd7b9d0d086816ab5f8f2d6ffea1dd3ea4 Mon Sep 17 00:00:00 2001
|
||||
> From: Alexander Botero-Lowry <alex.boterolowry@gmail.com>
|
||||
> Date: Tue, 17 Nov 2009 11:30:39 -0800
|
||||
> Subject: [PATCH] Deal with situation where sysconf(_SC_GETPW_R_SIZE_MAX) returns -1
|
||||
>
|
||||
> ---
|
||||
> notmuch-config.c | 2 ++
|
||||
> 1 files changed, 2 insertions(+), 0 deletions(-)
|
||||
>
|
||||
> diff --git a/notmuch-config.c b/notmuch-config.c
|
||||
> index 248149c..e7220d8 100644
|
||||
> --- a/notmuch-config.c
|
||||
> +++ b/notmuch-config.c
|
||||
> @@ -77,6 +77,7 @@ static char *
|
||||
> get_name_from_passwd_file (void *ctx)
|
||||
> {
|
||||
> long pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
> + if (pw_buf_size == -1) pw_buf_size = 64;
|
||||
> char *pw_buf = talloc_zero_size (ctx, pw_buf_size);
|
||||
> struct passwd passwd, *ignored;
|
||||
> char *name;
|
||||
> @@ -101,6 +102,7 @@ static char *
|
||||
> get_username_from_passwd_file (void *ctx)
|
||||
> {
|
||||
> long pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
|
||||
> + if (pw_buf_size == -1) pw_buf_size = 64;
|
||||
> char *pw_buf = talloc_zero_size (ctx, pw_buf_size);
|
||||
> struct passwd passwd, *ignored;
|
||||
> char *name;
|
||||
> --
|
||||
> 1.6.5.2
|
||||
>
|
||||
> _______________________________________________
|
||||
> notmuch mailing list
|
||||
> notmuch@notmuchmail.org
|
||||
|
|
90
test/T355-smime.sh
Executable file
90
test/T355-smime.sh
Executable file
|
@ -0,0 +1,90 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
test_description='S/MIME signature verification and decryption'
|
||||
. ./test-lib.sh || exit 1
|
||||
|
||||
add_gpgsm_home ()
|
||||
{
|
||||
local fpr
|
||||
[ -d ${GNUPGHOME} ] && return
|
||||
mkdir -m 0700 "$GNUPGHOME"
|
||||
gpgsm --no-tty --no-common-certs-import --disable-dirmngr --import < $TEST_DIRECTORY/smime/test.crt >"$GNUPGHOME"/import.log 2>&1
|
||||
fpr=$(gpgsm --list-key test_suite@notmuchmail.org | sed -n 's/.*fingerprint: //p')
|
||||
echo "$fpr S relax" >> $GNUPGHOME/trustlist.txt
|
||||
test_debug "cat $GNUPGHOME/import.log"
|
||||
}
|
||||
|
||||
test_require_external_prereq openssl
|
||||
test_require_external_prereq gpgsm
|
||||
|
||||
cp $TEST_DIRECTORY/smime/key+cert.pem test_suite.pem
|
||||
|
||||
FINGERPRINT=$(openssl x509 -fingerprint -in test_suite.pem -noout | sed -e 's/^.*=//' -e s/://g)
|
||||
|
||||
add_gpgsm_home
|
||||
|
||||
test_expect_success 'emacs delivery of S/MIME signed message' \
|
||||
'emacs_fcc_message \
|
||||
"test signed message 001" \
|
||||
"This is a test signed message." \
|
||||
"(mml-secure-message-sign \"smime\")"'
|
||||
|
||||
# Hard code the MML to avoid several interactive questions
|
||||
test_expect_success 'emacs delivery of S/MIME encrypted + signed message' \
|
||||
'emacs_fcc_message \
|
||||
"test encrypted message 001" \
|
||||
"<#secure method=smime mode=signencrypt keyfile=\\\"test_suite.pem\\\" certfile=\\\"test_suite.pem\\\">\nThis is a test encrypted message.\n"'
|
||||
|
||||
test_begin_subtest "Signature verification (openssl)"
|
||||
notmuch show --format=raw subject:"test signed message 001" |\
|
||||
openssl smime -verify -CAfile $TEST_DIRECTORY/smime/test.crt 2>OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
Verification successful
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "signature verification (notmuch CLI)"
|
||||
output=$(notmuch show --format=json --verify subject:"test signed message 001" \
|
||||
| notmuch_json_show_sanitize \
|
||||
| sed -e 's|"created": [-1234567890]*|"created": 946728000|' \
|
||||
-e 's|"expires": [-1234567890]*|"expires": 424242424|' )
|
||||
expected='[[[{"id": "XXXXX",
|
||||
"match": true,
|
||||
"excluded": false,
|
||||
"filename": "YYYYY",
|
||||
"timestamp": 946728000,
|
||||
"date_relative": "2000-01-01",
|
||||
"tags": ["inbox","signed"],
|
||||
"headers": {"Subject": "test signed message 001",
|
||||
"From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
|
||||
"To": "test_suite@notmuchmail.org",
|
||||
"Date": "Sat, 01 Jan 2000 12:00:00 +0000"},
|
||||
"body": [{"id": 1,
|
||||
"sigstatus": [{"status": "good",
|
||||
"fingerprint": "'$FINGERPRINT'",
|
||||
"expires": 424242424,
|
||||
"created": 946728000}],
|
||||
"content-type": "multipart/signed",
|
||||
"content": [{"id": 2,
|
||||
"content-type": "text/plain",
|
||||
"content": "This is a test signed message.\n"},
|
||||
{"id": 3,
|
||||
"content-length": 1922,
|
||||
"content-transfer-encoding": "base64",
|
||||
"content-type": "application/x-pkcs7-signature",
|
||||
"filename": "smime.p7s"}]}]},
|
||||
[]]]]'
|
||||
test_expect_equal_json \
|
||||
"$output" \
|
||||
"$expected"
|
||||
|
||||
test_begin_subtest "Decryption and signature verification (openssl)"
|
||||
notmuch show --format=raw subject:"test encrypted message 001" |\
|
||||
openssl smime -decrypt -recip test_suite.pem |\
|
||||
openssl smime -verify -CAfile $TEST_DIRECTORY/smime/test.crt 2>OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
Verification successful
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_done
|
|
@ -15,11 +15,11 @@ test_begin_subtest 'running test' run_test
|
|||
mkdir -p ${PWD}/fakedb/.notmuch
|
||||
( LD_LIBRARY_PATH="$TEST_DIRECTORY/../lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" \
|
||||
$TEST_DIRECTORY/symbol-test ${PWD}/fakedb ${PWD}/nonexistent \
|
||||
2>&1 | sed "s,${PWD},CWD,g") > OUTPUT
|
||||
2>&1 | notmuch_dir_sanitize | sed -e "s,\`,\',g" -e "s,${NOTMUCH_DEFAULT_XAPIAN_BACKEND},backend,g") > OUTPUT
|
||||
|
||||
cat <<EOF > EXPECTED
|
||||
A Xapian exception occurred opening database: Couldn't stat 'CWD/fakedb/.notmuch/xapian'
|
||||
caught No chert database found at path \`CWD/nonexistent'
|
||||
caught No backend database found at path 'CWD/nonexistent'
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
|
|
|
@ -116,25 +116,4 @@ MAIL_DIR/bar/new/21:2,
|
|||
MAIL_DIR/bar/new/22:2,
|
||||
MAIL_DIR/cur/51:2,"
|
||||
|
||||
# Ghost messages are difficult to test since they're nearly invisible.
|
||||
# However, if the upgrade works correctly, the ghost message should
|
||||
# retain the right thread ID even if all of the original messages in
|
||||
# the thread are deleted. That's what we test. This won't detect if
|
||||
# the upgrade just plain didn't happen, but it should detect if
|
||||
# something went wrong.
|
||||
test_begin_subtest "ghost message retains thread ID"
|
||||
# Upgrade database
|
||||
notmuch new
|
||||
# Get thread ID of real message
|
||||
thread=$(notmuch search --output=threads id:4EFC743A.3060609@april.org)
|
||||
# Delete all real messages in that thread
|
||||
rm $(notmuch search --output=files $thread)
|
||||
notmuch new
|
||||
# "Deliver" ghost message
|
||||
add_message '[subject]=Ghost' '[id]=4EFC3931.6030007@april.org'
|
||||
# If the ghost upgrade worked, the new message should be attached to
|
||||
# the existing thread ID.
|
||||
nthread=$(notmuch search --output=threads id:4EFC3931.6030007@april.org)
|
||||
test_expect_equal "$thread" "$nthread"
|
||||
|
||||
test_done
|
||||
|
|
|
@ -183,13 +183,13 @@ int main (int argc, char** argv)
|
|||
EOF
|
||||
cat <<'EOF' >EXPECTED
|
||||
== stdout ==
|
||||
Path already exists: CWD/mail
|
||||
Path already exists: MAIL_DIR
|
||||
|
||||
== stderr ==
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
cat <<'EOF' > c_head
|
||||
cat <<EOF > c_head
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
@ -202,16 +202,20 @@ int main (int argc, char** argv)
|
|||
notmuch_database_t *db;
|
||||
notmuch_status_t stat;
|
||||
char *path;
|
||||
char *msg = NULL;
|
||||
int fd;
|
||||
|
||||
stat = notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db);
|
||||
stat = notmuch_database_open_verbose (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db, &msg);
|
||||
if (stat != NOTMUCH_STATUS_SUCCESS) {
|
||||
fprintf (stderr, "error opening database: %d\n", stat);
|
||||
fprintf (stderr, "error opening database: %d %s\n", stat, msg ? msg : "");
|
||||
exit (1);
|
||||
}
|
||||
path = talloc_asprintf (db, "%s/.notmuch/xapian/postlist.DB", argv[1]);
|
||||
path = talloc_asprintf (db, "%s/.notmuch/xapian/postlist.${db_ending}", argv[1]);
|
||||
fd = open(path,O_WRONLY|O_TRUNC);
|
||||
if (fd < 0)
|
||||
fprintf (stderr, "error opening %s\n");
|
||||
if (fd < 0) {
|
||||
fprintf (stderr, "error opening %s\n", argv[1]);
|
||||
exit (1);
|
||||
}
|
||||
EOF
|
||||
cat <<'EOF' > c_tail
|
||||
if (stat) {
|
||||
|
|
42
test/T580-thread-search.sh
Executable file
42
test/T580-thread-search.sh
Executable file
|
@ -0,0 +1,42 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Copyright (c) 2015 David Bremner
|
||||
#
|
||||
|
||||
test_description='test of searching by thread-id'
|
||||
|
||||
. ./test-lib.sh || exit 1
|
||||
|
||||
add_email_corpus
|
||||
|
||||
test_begin_subtest "Every message is found in exactly one thread"
|
||||
|
||||
count=0
|
||||
success=0
|
||||
for id in $(notmuch search --output=messages '*'); do
|
||||
count=$((count +1))
|
||||
matches=$(notmuch search --output=threads "$id" | wc -l)
|
||||
if [ "$matches" = 1 ]; then
|
||||
success=$((success + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
test_expect_equal "$count" "$success"
|
||||
|
||||
test_begin_subtest "roundtripping message-ids via thread-ids"
|
||||
|
||||
count=0
|
||||
success=0
|
||||
for id in $(notmuch search --output=messages '*'); do
|
||||
count=$((count +1))
|
||||
thread=$(notmuch search --output=threads "$id")
|
||||
matched=$(notmuch search --output=messages "$thread" | grep "$id")
|
||||
if [ "$matched" = "$id" ]; then
|
||||
success=$((success + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
test_expect_equal "$count" "$success"
|
||||
|
||||
|
||||
test_done
|
124
test/T590-thread-breakage.sh
Executable file
124
test/T590-thread-breakage.sh
Executable file
|
@ -0,0 +1,124 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Copyright (c) 2016 Daniel Kahn Gillmor
|
||||
#
|
||||
|
||||
test_description='thread breakage during reindexing
|
||||
|
||||
notmuch uses ghost documents to track messages we have seen references
|
||||
to but have never seen. Regardless of the order of delivery, message
|
||||
deletion, and reindexing, the list of ghost messages for a given
|
||||
stored corpus should not vary, so that threads can be reassmebled
|
||||
cleanly.
|
||||
|
||||
In practice, we accept a small amount of variation (and therefore
|
||||
traffic pattern metadata leakage to be stored in the index) for the
|
||||
sake of efficiency.
|
||||
|
||||
This test also embeds some subtests to ensure that indexing actually
|
||||
works properly and attempted fixes to threading issues do not break
|
||||
the expected contents of the index.'
|
||||
|
||||
. ./test-lib.sh || exit 1
|
||||
|
||||
message_a() {
|
||||
mkdir -p ${MAIL_DIR}/cur
|
||||
cat > ${MAIL_DIR}/cur/a <<EOF
|
||||
Subject: First message
|
||||
Message-ID: <a@example.net>
|
||||
From: Alice <alice@example.net>
|
||||
To: Bob <bob@example.net>
|
||||
Date: Thu, 31 Mar 2016 20:10:00 -0400
|
||||
|
||||
This is the first message in the thread.
|
||||
Apple
|
||||
EOF
|
||||
}
|
||||
|
||||
message_b() {
|
||||
mkdir -p ${MAIL_DIR}/cur
|
||||
cat > ${MAIL_DIR}/cur/b <<EOF
|
||||
Subject: Second message
|
||||
Message-ID: <b@example.net>
|
||||
In-Reply-To: <a@example.net>
|
||||
References: <a@example.net>
|
||||
From: Bob <bob@example.net>
|
||||
To: Alice <alice@example.net>
|
||||
Date: Thu, 31 Mar 2016 20:15:00 -0400
|
||||
|
||||
This is the second message in the thread.
|
||||
Banana
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
test_content_count() {
|
||||
test_begin_subtest "${3:-looking for $2 instance of '$1'}"
|
||||
count=$(notmuch count --output=threads "$1")
|
||||
test_expect_equal "$count" "$2"
|
||||
}
|
||||
|
||||
test_thread_count() {
|
||||
test_begin_subtest "${2:-Expecting $1 thread(s)}"
|
||||
count=$(notmuch count --output=threads)
|
||||
test_expect_equal "$count" "$1"
|
||||
}
|
||||
|
||||
test_ghost_count() {
|
||||
test_begin_subtest "${2:-Expecting $1 ghosts(s)}"
|
||||
ghosts=$(../ghost-report ${MAIL_DIR}/.notmuch/xapian)
|
||||
test_expect_equal "$ghosts" "$1"
|
||||
}
|
||||
|
||||
notmuch new >/dev/null
|
||||
|
||||
test_thread_count 0 'There should be no threads initially'
|
||||
test_ghost_count 0 'There should be no ghosts initially'
|
||||
|
||||
message_a
|
||||
notmuch new >/dev/null
|
||||
test_thread_count 1 'One message in: one thread'
|
||||
test_content_count apple 1
|
||||
test_content_count banana 0
|
||||
test_ghost_count 0
|
||||
|
||||
message_b
|
||||
notmuch new >/dev/null
|
||||
test_thread_count 1 'Second message in the same thread: one thread'
|
||||
test_content_count apple 1
|
||||
test_content_count banana 1
|
||||
test_ghost_count 0
|
||||
|
||||
rm -f ${MAIL_DIR}/cur/a
|
||||
notmuch new >/dev/null
|
||||
test_thread_count 1 'First message removed: still only one thread'
|
||||
test_content_count apple 0
|
||||
test_content_count banana 1
|
||||
test_ghost_count 1 'should be one ghost after first message removed'
|
||||
|
||||
message_a
|
||||
notmuch new >/dev/null
|
||||
test_thread_count 1 'First message reappears: should return to the same thread'
|
||||
test_content_count apple 1
|
||||
test_content_count banana 1
|
||||
test_ghost_count 0
|
||||
|
||||
rm -f ${MAIL_DIR}/cur/b
|
||||
notmuch new >/dev/null
|
||||
test_thread_count 1 'Removing second message: still only one thread'
|
||||
test_content_count apple 1
|
||||
test_content_count banana 0
|
||||
test_begin_subtest 'No ghosts should remain after deletion of second message'
|
||||
# this is known to fail; we are leaking ghost messages deliberately
|
||||
test_subtest_known_broken
|
||||
ghosts=$(../ghost-report ${MAIL_DIR}/.notmuch/xapian)
|
||||
test_expect_equal "$ghosts" "0"
|
||||
|
||||
rm -f ${MAIL_DIR}/cur/a
|
||||
notmuch new >/dev/null
|
||||
test_thread_count 0 'All messages gone: no threads'
|
||||
test_content_count apple 0
|
||||
test_content_count banana 0
|
||||
test_ghost_count 0 'No ghosts should remain after full thread deletion'
|
||||
|
||||
test_done
|
|
@ -29,16 +29,19 @@ class RenameBreakpoint(gdb.Breakpoint):
|
|||
self.n = 0
|
||||
|
||||
def stop(self):
|
||||
# As an optimization, only consider snapshots after a Xapian
|
||||
# has really committed. Xapian overwrites record.base? as the
|
||||
# last step in the commit, so keep an eye on their inumbers.
|
||||
inodes = {}
|
||||
for path in glob.glob('%s/.notmuch/xapian/record.base*' % maildir):
|
||||
inodes[path] = os.stat(path).st_ino
|
||||
if inodes == self.last_inodes:
|
||||
# Continue
|
||||
return False
|
||||
self.last_inodes = inodes
|
||||
xapiandir = '%s/.notmuch/xapian' % maildir
|
||||
if os.path.isfile('%s/iamchert' % xapiandir):
|
||||
# As an optimization, only consider snapshots after a
|
||||
# Xapian has really committed. The chert backend
|
||||
# overwrites record.base? as the last step in the commit,
|
||||
# so keep an eye on their inumbers.
|
||||
inodes = {}
|
||||
for path in glob.glob('%s/record.base*' % xapiandir):
|
||||
inodes[path] = os.stat(path).st_ino
|
||||
if inodes == self.last_inodes:
|
||||
# Continue
|
||||
return False
|
||||
self.last_inodes = inodes
|
||||
|
||||
# Save a backtrace in case the test does fail
|
||||
backtrace = gdb.execute('backtrace', to_string=True)
|
||||
|
|
|
@ -5,6 +5,7 @@ Date: Tue, 17 Nov 2009 14:00:54 -0500
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
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
|
||||
|
@ -39,6 +40,7 @@ Cheers,
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
> See the patch just posted here.
|
||||
|
||||
|
@ -65,6 +67,7 @@ Cheers,
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
> I've also pushed a slightly more complicated (and complete) fix to my
|
||||
> private notmuch repository
|
||||
|
|
|
@ -5,6 +5,7 @@ Date: Tue, 17 Nov 2009 14:00:54 -0500
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
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
|
||||
|
@ -38,6 +39,7 @@ Cheers,
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
> See the patch just posted here.
|
||||
|
||||
|
@ -62,6 +64,7 @@ Cheers,
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
> I've also pushed a slightly more complicated (and complete) fix to my
|
||||
> private notmuch repository
|
||||
|
|
|
@ -5,6 +5,7 @@ Date: Tue, 17 Nov 2009 14:00:54 -0500
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
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
|
||||
|
@ -39,6 +40,7 @@ Date: Tue, 17 Nov 2009 15:33:01 -0500
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
> See the patch just posted here.
|
||||
|
||||
|
@ -65,6 +67,7 @@ Date: Tue, 17 Nov 2009 19:50:40 -0500
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
> I've also pushed a slightly more complicated (and complete) fix to my
|
||||
> private notmuch repository
|
||||
|
|
|
@ -9,6 +9,7 @@ Subject: [notmuch] Working with Maildir storage?
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
> See the patch just posted here.
|
||||
|
||||
|
|
|
@ -8,8 +8,5 @@ Search: .
|
|||
|
||||
All tags: [show]
|
||||
|
||||
Type a search query and hit RET to view matching threads.
|
||||
Edit saved searches with the `edit' button.
|
||||
Hit RET or click on a saved search or tag name to view matching threads.
|
||||
`=' to refresh this screen. `s' to search messages. `q' to quit.
|
||||
Customize this page.
|
||||
Hit `?' for context-sensitive help in any Notmuch screen.
|
||||
Customize Notmuch or this page.
|
||||
|
|
|
@ -11,8 +11,5 @@ All tags: [hide]
|
|||
52 a-very-long-tag 52 inbox 52 unread
|
||||
4 attachment 7 signed
|
||||
|
||||
Type a search query and hit RET to view matching threads.
|
||||
Edit saved searches with the `edit' button.
|
||||
Hit RET or click on a saved search or tag name to view matching threads.
|
||||
`=' to refresh this screen. `s' to search messages. `q' to quit.
|
||||
Customize this page.
|
||||
Hit `?' for context-sensitive help in any Notmuch screen.
|
||||
Customize Notmuch or this page.
|
||||
|
|
|
@ -4,8 +4,5 @@ Search: .
|
|||
|
||||
All tags: [show]
|
||||
|
||||
Type a search query and hit RET to view matching threads.
|
||||
Edit saved searches with the `edit' button.
|
||||
Hit RET or click on a saved search or tag name to view matching threads.
|
||||
`=' to refresh this screen. `s' to search messages. `q' to quit.
|
||||
Customize this page.
|
||||
Hit `?' for context-sensitive help in any Notmuch screen.
|
||||
Customize Notmuch or this page.
|
||||
|
|
|
@ -8,8 +8,5 @@ Search: .
|
|||
|
||||
All tags: [show]
|
||||
|
||||
Type a search query and hit RET to view matching threads.
|
||||
Edit saved searches with the `edit' button.
|
||||
Hit RET or click on a saved search or tag name to view matching threads.
|
||||
`=' to refresh this screen. `s' to search messages. `q' to quit.
|
||||
Customize this page.
|
||||
Hit `?' for context-sensitive help in any Notmuch screen.
|
||||
Customize Notmuch or this page.
|
||||
|
|
|
@ -5,6 +5,7 @@ Date: Tue, 17 Nov 2009 14:00:54 -0500
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
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
|
||||
|
@ -45,6 +46,7 @@ http://notmuchmail.org/mailman/listinfo/notmuch
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
|
||||
Twas brillig at 14:00:54 17.11.2009 UTC-05 when lars@seas.harvard.edu did
|
||||
|
@ -77,6 +79,7 @@ http://notmuchmail.org/mailman/listinfo/notmuch
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
> See the patch just posted here.
|
||||
|
||||
|
@ -159,6 +162,7 @@ http://notmuchmail.org/mailman/listinfo/notmuch
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
> I've also pushed a slightly more complicated (and complete) fix to my
|
||||
> private notmuch repository
|
||||
|
|
|
@ -5,6 +5,7 @@ Date: Tue, 17 Nov 2009 14:00:54 -0500
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
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
|
||||
|
@ -45,6 +46,7 @@ http://notmuchmail.org/mailman/listinfo/notmuch
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
|
||||
Twas brillig at 14:00:54 17.11.2009 UTC-05 when lars@seas.harvard.edu did
|
||||
|
@ -77,6 +79,7 @@ http://notmuchmail.org/mailman/listinfo/notmuch
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
> See the patch just posted here.
|
||||
|
||||
|
@ -159,6 +162,7 @@ http://notmuchmail.org/mailman/listinfo/notmuch
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
> I've also pushed a slightly more complicated (and complete) fix to my
|
||||
> private notmuch repository
|
||||
|
|
|
@ -5,6 +5,7 @@ Date: Tue, 17 Nov 2009 14:00:54 -0500
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
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
|
||||
|
@ -45,6 +46,7 @@ Date: Wed, 18 Nov 2009 01:02:38 +0600
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
|
||||
Twas brillig at 14:00:54 17.11.2009 UTC-05 when lars@seas.harvard.edu did
|
||||
|
@ -77,6 +79,7 @@ Date: Tue, 17 Nov 2009 15:33:01 -0500
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
> See the patch just posted here.
|
||||
|
||||
|
@ -159,6 +162,7 @@ Date: Tue, 17 Nov 2009 19:50:40 -0500
|
|||
|
||||
[ multipart/mixed ]
|
||||
[ multipart/signed ]
|
||||
[ Unknown signature status ]
|
||||
[ text/plain ]
|
||||
> I've also pushed a slightly more complicated (and complete) fix to my
|
||||
> private notmuch repository
|
||||
|
|
14
test/ghost-report.cc
Normal file
14
test/ghost-report.cc
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <xapian.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
if (argc < 2) {
|
||||
std::cerr << "usage: ghost-report xapian-dir" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Xapian::Database db(argv[1]);
|
||||
std::cout << db.get_termfreq("Tghost") << std::endl;
|
||||
}
|
7
test/smime/README
Normal file
7
test/smime/README
Normal file
|
@ -0,0 +1,7 @@
|
|||
test.crt: self signed certificated
|
||||
% gpgsm --gen-key # needs gpgsm 2.1
|
||||
|
||||
key+cert.pem: cert + unencryped private
|
||||
% gpsm --import test.crt
|
||||
% gpgsm --export-private-key-p12 -out foo.p12 (no passphrase)
|
||||
% openssl pkcs12 -in ns.p12 -clcerts -nodes > key+cert.pem
|
56
test/smime/key+cert.pem
Normal file
56
test/smime/key+cert.pem
Normal file
|
@ -0,0 +1,56 @@
|
|||
Bag Attributes
|
||||
friendlyName: GnuPG exported certificate e0972a47
|
||||
localKeyID: 61 6F 46 CD 73 83 4C 63 84 77 56 AF 0D FB 64 A6 E0 97 2A 47
|
||||
subject=/CN=Notmuch Test Suite
|
||||
issuer=/CN=Notmuch Test Suite
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDCzCCAfOgAwIBAgIIb3SMlL0MZ6kwDQYJKoZIhvcNAQELBQAwHTEbMBkGA1UE
|
||||
AxMSTm90bXVjaCBUZXN0IFN1aXRlMCAXDTE1MTIxNDAyMDgxMFoYDzIwNjMwNDA1
|
||||
MTcwMDAwWjAdMRswGQYDVQQDExJOb3RtdWNoIFRlc3QgU3VpdGUwggEiMA0GCSqG
|
||||
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7vH1/lkENTAJRbyq2036K7Pw+imSIhB5T
|
||||
U0WnAgVGWOemY1Eppi9Dk6rjDxuuUKOCQ5el2wmFZN57Fi/4leBH7x217BnnqWNU
|
||||
QV88DxEfV+sk8dSb4a5FOOyfhFJmZso/0lK8x0fBcCNjmRFIjB1afSSXWnCvRpAR
|
||||
v+O9trLJuIjbbmXg1gltjuB5yDw8/OLEI7G7YSIop9FxopWJL5rW/o2WEfRPGpYe
|
||||
HNRLObCRIvbyDd6XjaCrKBuIrhN7R7mmIa9PUyl8TiY+pCMWs9dHmOsiC73/+P6E
|
||||
AhsTOY1bfbGQXBAGZ/FL+SgC5wEcPr2u3+y8y5gw2bpaVhQnu6YLAgMBAAGjTTBL
|
||||
MCUGA1UdEQQeMByBGnRlc3Rfc3VpdGVAbm90bXVjaG1haWwub3JnMBEGCisGAQQB
|
||||
2kcCAgEEAwEB/zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBb
|
||||
XP5OnRVplrEdlnivx3CbCLWO13fcMWXfvKxLGsKFwKuxtpvINFUKM+jDr0kVdQ3d
|
||||
u3DJe2hNFQMILK/KrGyN5qEz2YBdHNvdkkvWA+3WHr/tiNr6Rly6QuxBzouxzmRu
|
||||
MmnUhsOzZaHT3GmLSVJlwie8KqSfKVGwyBmCyHbUQkMrSEV6QDESN6KyWt85gokB
|
||||
56Bc/wVq073xS1nFbfF1M3Z5q5BlLZK4IOerKTQx/oSfR4EX6B7rW2pttWsUCyEj
|
||||
LljaA8ehxR9B29m08IGGl43pHEpC1WnOHvsEGs99mPpjWbUgVv5KY7OuS/8iVw6v
|
||||
/Yy5Z+JBwlMzTBaUXXl3
|
||||
-----END CERTIFICATE-----
|
||||
Bag Attributes
|
||||
friendlyName: GnuPG exported certificate e0972a47
|
||||
localKeyID: 61 6F 46 CD 73 83 4C 63 84 77 56 AF 0D FB 64 A6 E0 97 2A 47
|
||||
Key Attributes: <No Attributes>
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7vH1/lkENTAJR
|
||||
byq2036K7Pw+imSIhB5TU0WnAgVGWOemY1Eppi9Dk6rjDxuuUKOCQ5el2wmFZN57
|
||||
Fi/4leBH7x217BnnqWNUQV88DxEfV+sk8dSb4a5FOOyfhFJmZso/0lK8x0fBcCNj
|
||||
mRFIjB1afSSXWnCvRpARv+O9trLJuIjbbmXg1gltjuB5yDw8/OLEI7G7YSIop9Fx
|
||||
opWJL5rW/o2WEfRPGpYeHNRLObCRIvbyDd6XjaCrKBuIrhN7R7mmIa9PUyl8TiY+
|
||||
pCMWs9dHmOsiC73/+P6EAhsTOY1bfbGQXBAGZ/FL+SgC5wEcPr2u3+y8y5gw2bpa
|
||||
VhQnu6YLAgMBAAECggEAVhtHCHz3C01Ahu9RDRgGI1w8+cZqA/9tFVTNTqNrne9r
|
||||
GHLXKB4z8W/KYmhsjtAnnri31neXb1prfNMZX5AGlZfD7cwDubCEgYGWV6qldNXT
|
||||
YVeV54VkdBV+2k9Lp/Ifc5RZJILWk4+Ge8kaF0dEs1tQrCbsJkhcDfgQUdR5PnGe
|
||||
6cKv/8HJo0ep6u5cJloIluit8yF3z4+aHixMQBvQKm/8tug+EsrQZ3IVXbh1hONO
|
||||
AZ68z9CrU2pJ/0w/jwwcM5feRfTMC7bZ3vkQb1mQKYFJrvN77TGroUtAZFWqJw7M
|
||||
r0f2MShdVjfEdJ1ySnCyKF24cSSPSQsLZUe4UlFyQQKBgQDlqr9ajaUzc6Lyma2e
|
||||
Q1IJapbX2OZQtf5tlKVCVtZOlu5r97YMOK96XsQFKtdxhAhrGvvTJwPmwhj+fqfR
|
||||
XltNrmUBpHCMsm9nloADvBS83KTP5tw9TMT0VZpt+m5XmvutdyQbSKwy+KMy+GZz
|
||||
/XBQCfTEoiDS4grGFftvZuRB4QKBgQDRQvsVFMh2NOnVGqczHJNGjvbDueUJmPUN
|
||||
3VxZc/FpBGLRSoN7uxQ4dGNnwyvXHs+pLAAC6xZpFCos9c3R8EPvoMyUehoDSAKW
|
||||
CMD4C+K8z7n4ducE5a0NrGIgQvnXtteKr3ZwK8V7cscyTCyjXdrQmQ5XHeue8asR
|
||||
758g+dG9awKBgEWuZJho2XKe5xWMIu0dp8pLmLCsklRyo1tD+lACYMs/Z99CLO3Q
|
||||
VQ1fq0GWGf/K+3LjoPwTnk9pHIQ6kVgotLMA8oxpA+zsRni7ZOO9MN2MZETf2nqO
|
||||
zEMFpfEwRkI2N54Nw9qzVeuxHHLegtc2Udk27BisyCCzjGlFSiAmq6KBAoGAFGfE
|
||||
RXjcvT65HX8Gaya+wtugFB8BRx0JX7dI6OLk5ZKLmq0ykH2bQepgnWermmU4we77
|
||||
0Dvtfa3u0YjZ/24XXg2YbSpWiWps0Y2/C7AyAAzq12/1OGcX5qk4Tbd0f+QkIset
|
||||
qxzmt4XcAKw50J+Vf3DmbYQ1M/BftCZcTm0ShHcCgYEAxp8mjE8iIHxFrm7nHMS0
|
||||
2/iWxO8DYaAZ0OLfjaZELHchVvTwa+DynbkwvOc3l4cbNTVaf9O6nmHTkLyBLBNr
|
||||
2htPKm1vi9TzNdvGqobFO3ijfvdGvq1rjQl86ns0cf395REmEaVX3zcw2v+GyC5n
|
||||
qE6Aa5bvdZ9Yykg6aoFo1mY=
|
||||
-----END PRIVATE KEY-----
|
19
test/smime/test.crt
Normal file
19
test/smime/test.crt
Normal file
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDCzCCAfOgAwIBAgIIb3SMlL0MZ6kwDQYJKoZIhvcNAQELBQAwHTEbMBkGA1UE
|
||||
AxMSTm90bXVjaCBUZXN0IFN1aXRlMCAXDTE1MTIxNDAyMDgxMFoYDzIwNjMwNDA1
|
||||
MTcwMDAwWjAdMRswGQYDVQQDExJOb3RtdWNoIFRlc3QgU3VpdGUwggEiMA0GCSqG
|
||||
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7vH1/lkENTAJRbyq2036K7Pw+imSIhB5T
|
||||
U0WnAgVGWOemY1Eppi9Dk6rjDxuuUKOCQ5el2wmFZN57Fi/4leBH7x217BnnqWNU
|
||||
QV88DxEfV+sk8dSb4a5FOOyfhFJmZso/0lK8x0fBcCNjmRFIjB1afSSXWnCvRpAR
|
||||
v+O9trLJuIjbbmXg1gltjuB5yDw8/OLEI7G7YSIop9FxopWJL5rW/o2WEfRPGpYe
|
||||
HNRLObCRIvbyDd6XjaCrKBuIrhN7R7mmIa9PUyl8TiY+pCMWs9dHmOsiC73/+P6E
|
||||
AhsTOY1bfbGQXBAGZ/FL+SgC5wEcPr2u3+y8y5gw2bpaVhQnu6YLAgMBAAGjTTBL
|
||||
MCUGA1UdEQQeMByBGnRlc3Rfc3VpdGVAbm90bXVjaG1haWwub3JnMBEGCisGAQQB
|
||||
2kcCAgEEAwEB/zAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBb
|
||||
XP5OnRVplrEdlnivx3CbCLWO13fcMWXfvKxLGsKFwKuxtpvINFUKM+jDr0kVdQ3d
|
||||
u3DJe2hNFQMILK/KrGyN5qEz2YBdHNvdkkvWA+3WHr/tiNr6Rly6QuxBzouxzmRu
|
||||
MmnUhsOzZaHT3GmLSVJlwie8KqSfKVGwyBmCyHbUQkMrSEV6QDESN6KyWt85gokB
|
||||
56Bc/wVq073xS1nFbfF1M3Z5q5BlLZK4IOerKTQx/oSfR4EX6B7rW2pttWsUCyEj
|
||||
LljaA8ehxR9B29m08IGGl43pHEpC1WnOHvsEGs99mPpjWbUgVv5KY7OuS/8iVw6v
|
||||
/Yy5Z+JBwlMzTBaUXXl3
|
||||
-----END CERTIFICATE-----
|
|
@ -48,7 +48,7 @@ restore_database () {
|
|||
|
||||
# Test the binaries we have just built. The tests are kept in
|
||||
# test/ subdirectory and are run in 'trash directory' subdirectory.
|
||||
TEST_DIRECTORY=$(pwd)
|
||||
TEST_DIRECTORY=$(pwd -P)
|
||||
notmuch_path=`find_notmuch_path "$TEST_DIRECTORY"`
|
||||
|
||||
# configure output
|
||||
|
|
|
@ -188,3 +188,13 @@ nothing."
|
|||
;; environments
|
||||
|
||||
(setq mm-text-html-renderer 'html2text)
|
||||
|
||||
;; Set some variables for S/MIME tests.
|
||||
|
||||
(setq smime-keys '(("" "test_suite.pem" nil)))
|
||||
|
||||
(setq mml-smime-use 'openssl)
|
||||
|
||||
;; all test keys are without passphrase
|
||||
(eval-after-load 'smime
|
||||
'(defun smime-ask-passphrase (cache) nil))
|
||||
|
|
|
@ -82,6 +82,9 @@ unset CDPATH
|
|||
|
||||
unset GREP_OPTIONS
|
||||
|
||||
# For emacsclient
|
||||
unset ALTERNATE_EDITOR
|
||||
|
||||
# Convenience
|
||||
#
|
||||
# A regexp to match 5 and 40 hexdigits
|
||||
|
@ -675,9 +678,14 @@ notmuch_search_sanitize ()
|
|||
perl -pe 's/("?thread"?: ?)("?)................("?)/\1\2XXX\3/'
|
||||
}
|
||||
|
||||
notmuch_search_files_sanitize()
|
||||
notmuch_search_files_sanitize ()
|
||||
{
|
||||
sed -e "s,$MAIL_DIR,MAIL_DIR,"
|
||||
notmuch_dir_sanitize
|
||||
}
|
||||
|
||||
notmuch_dir_sanitize ()
|
||||
{
|
||||
sed -e "s,$MAIL_DIR,MAIL_DIR," -e "s,${PWD},CWD,g" "$@"
|
||||
}
|
||||
|
||||
NOTMUCH_SHOW_FILENAME_SQUELCH='s,filename:.*/mail,filename:/XXX/mail,'
|
||||
|
@ -1177,7 +1185,7 @@ test_C () {
|
|||
echo "== stdout ==" > OUTPUT.stdout
|
||||
echo "== stderr ==" > OUTPUT.stderr
|
||||
./${exec_file} "$@" 1>>OUTPUT.stdout 2>>OUTPUT.stderr
|
||||
sed "s,${PWD},CWD,g" OUTPUT.stdout OUTPUT.stderr > OUTPUT
|
||||
notmuch_dir_sanitize OUTPUT.stdout OUTPUT.stderr > OUTPUT
|
||||
}
|
||||
|
||||
|
||||
|
@ -1319,10 +1327,23 @@ test -z "$NO_PYTHON" && test_set_prereq PYTHON
|
|||
ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS
|
||||
rm -f y
|
||||
|
||||
# convert variable from configure to more convenient form
|
||||
case "$NOTMUCH_DEFAULT_XAPIAN_BACKEND" in
|
||||
glass)
|
||||
db_ending=glass
|
||||
;;
|
||||
chert)
|
||||
db_ending=DB
|
||||
;;
|
||||
*)
|
||||
error "Unknown Xapian backend $NOTMUCH_DEFAULT_XAPIAN_BACKEND"
|
||||
esac
|
||||
# declare prerequisites for external binaries used in tests
|
||||
test_declare_external_prereq dtach
|
||||
test_declare_external_prereq emacs
|
||||
test_declare_external_prereq ${TEST_EMACSCLIENT}
|
||||
test_declare_external_prereq gdb
|
||||
test_declare_external_prereq gpg
|
||||
test_declare_external_prereq openssl
|
||||
test_declare_external_prereq gpgsm
|
||||
test_declare_external_prereq ${NOTMUCH_PYTHON}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue