Add option to open a message in the message viewer without setting the
seen flag. Enables the message viewer to be used as a preview pane
without changing the message flags unintentionally. Before, the message
viewer would set the seen flag by default. The IMAP backend will now
always fetch the message body with the peek option enabled (same as we
fetch the headers).
An "auto-mark-read" option is added to the ui config which is set to
true by default. If set the false, the seen flag is not set by the
message viewer.
Co-authored-by: "James Cook" <falsifian@falsifian.org>
Signed-off-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
When an error is encountered fetching a header, the backends respond
with a type.Error worker message. On receipt of this message, the UI
deletes all pending headers. The headers are all requested again as they
remain on the screen, resulting in an infinite request loop - and an
infinite logging loop. The user only ever sees the spinner unless they
check the logs.
A previous commit intended to fix this, however it introduced a
regression where any message that was part of the fetch request would
also be marked as erroneous. This commit is reverted with commit
2aad2fea7d36 ("msgstore: revert 9fdc7acf5b48").
Send an erroneous message info message from the backend when an error is
encountered for a specific UID.
Fixes: 01f80721e2 ("msgstore: post MessageInfo on erroneous fetch")
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Acked-by: Robin Jarry <robin@jarry.cc>
Do not throw an error when the charset is unknown; the message entity
can still be read, but log the error instead.
Reported-by: falsifian
Signed-off-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
Error wrapping as introduced in Go 1.13 adds some additional logic to
use for comparing errors and adding information to it.
Signed-off-by: Moritz Poldrack <moritz@poldrack.dev>
Acked-by: Robin Jarry <robin@jarry.cc>
The imap worker's seqmap is represented as a map of sequence number to
UID. This presents a problem when expunging group of messages from the
mailbox: each individual expunge decrements the sequence numbers by 1
(for every sequence number greater than the expunged). This requires a
looping around the map to update the keys. The use of a map also
requires that both the sequence number and the UID of a message be known
in order to insert it into the map. This is only discovered by fetching
individual message body parts (flags, headers, etc), leaving the seqmap
to be empty until we have fetched information about each message. In
certain instances (if a mailbox has recently been loaded), all
information is loaded in memory and no new information is fetched -
leaving the seqmap empty and the UI out of sync with the worker.
Refactor the seqmap as a slice, so that any expunge automatically
decrements the rest of the sequences.
Use the results of FetchDirectoryContents or FetchDirectoryThreaded to
initialize the seqmap with all discovered UIDs. Sort the UIDs in
ascending order: IMAP specification requires that sequence numbers start
at 1 increase in order of ascending UID.
Add individual messages to the map if they come via a MessageUpdate and
have a sequence number larger than our slice.
Update seqmap tests with new logic.
Reference: https://datatracker.ietf.org/doc/html/rfc3501#section-2.3.1.2
Fixes: https://todo.sr.ht/~rjarry/aerc/69
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Acked-by: Robin Jarry <robin@jarry.cc>
Do not pass logger objects around anymore. Shuffle some messages to make
them consistent with the new logging API. Avoid using %v when a more
specific verb exists for the argument types.
The loggers are completely disabled (i.e. Sprintf is not even called)
by default. They are only enabled when redirecting stdout to a file.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Moritz Poldrack <moritz@poldrack.dev>
When opening unread emails from certain people (I won't name any names,
sorry), an annoying error message is displayed on the status line:
could not get section &imap.BodySectionName{BodyPartName:
imap.BodyPartName{Specifier:"", Path:[]int(nil), Fields:[]string(nil),
NotFields:false}, Peek:false, Partial:[]int(nil), value}
This does not occur for already read messages. This issue is similar to
the one that was fixed in commit 8ed95b0d2a ("imap: avoid crash when
replying to unread message").
This happens because the flags are updated in the callback that receives
the message itself. It causes the flag update to arrive in the same
channel/request. Ignore the messages that have an empty body (i.e. only
containing flag updates). This is inherently racy but there seems no way
to get rid of these extra messages.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Koni Marti <koni.marti@gmail.com>
There are concurrent threads that are accessing and modifying
IMAPWorker.seqMap (the mapping of sequence numbers to message UIDs).
This can lead to crashes when trying to add and remove a message ID.
panic: runtime error: index out of range [391] with length 390
goroutine 1834 [running]:
git.sr.ht/~rjarry/aerc/logging.PanicHandler()
logging/panic-logger.go:47 +0x6de
panic({0xa41760, 0xc0019b3290})
/usr/lib/golang/src/runtime/panic.go:838 +0x207
git.sr.ht/~rjarry/aerc/worker/imap.(*IMAPWorker).handleFetchMessages.func1()
worker/imap/fetch.go:214 +0x185
created by git.sr.ht/~rjarry/aerc/worker/imap.(*IMAPWorker).handleFetchMessages
worker/imap/fetch.go:209 +0x12b
Use a map which makes more sense than a simple array for random access
operations. Also, it allows better typing for the key values. Protect
the map with a mutex. Add internal API to access the map. Add basic unit
tests to ensure that concurrent access works.
Fixes: https://todo.sr.ht/~rjarry/aerc/49
Signed-off-by: Robin Jarry <robin@jarry.cc>
Acked-by: Moritz Poldrack <moritz@poldrack.dev>
Add option to cache headers for imap accounts. Cache db is located at
$XDG_CACHE_DIR/aerc/{account name}. The cache is cleaned of stale
entries when aerc is first opened.
Two new account level configuration options are introduced:
* cache-headers (Default: false)
* cache-max-age (Default: 30 days (720 hours))
The change in worker/imap/open.go is to set the selected directory. This
is required to access the UIDVALIDITY field, which is used in
combination with the message ID to form the key for use in the cache db.
The key structure is: "header.{UIDVALIDITY}.{UID}"
Where reasonable, cache does not stop aerc from running. In general, if
there is an error in the cache, aerc should continue working as usual.
Errors are either displayed to the user or logged.
All messages are stored without flags, and when retrieved have the flags
set to SEEN. This is to prevent UI flashes. A new method to
FetchMessageFlags is introduced to update flags of cached headers. This
is done asynchronously, and the user will see their messages appear and
then any flags updated. The message will initially show as SEEN, but
will update to unread. I considered updating the cache with the
last-known flag state, however it seems prudent to spare the R/W cycle
and assume that - eventually - all messages will end up read, and if it
isn't the update will occur rather quickly.
Note that leveldb puts a lock on the database, preventing multiple
instances of aerc from accessing the cache at the same time.
Much of this work is based on previous efforts by Vladimír Magyar.
Implements: https://todo.sr.ht/~rjarry/aerc/2
Thanks: Vladimír Magyar <vladimir@mgyar.me>
Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Tested-by: inwit <inwit@sindominio.net>
Reviewed-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
When running `:reply -q` on an unread message, aerc crashes after
opening the editor:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x5d1019]
goroutine 63 [running]:
bufio.(*Reader).fill(0xc000086ef8)
/usr/lib/golang/src/bufio/bufio.go:106 +0xd9
bufio.(*Reader).Peek(0xc00020bef8, 0x1)
/usr/lib/golang/src/bufio/bufio.go:144 +0x5d
github.com/emersion/go-message/textproto.ReadHeader(0xc00004a700?)
emersion/go-message@v0.15.0/textproto/header.go:525 +0x5f
git.sr.ht/~rjarry/aerc/worker/imap.(*IMAPWorker).handleFetchMessageBodyPart.func1(0xc00056e280)
worker/imap/fetch.go:99 +0x1ab
git.sr.ht/~rjarry/aerc/worker/imap.(*IMAPWorker).handleFetchMessages.func1()
worker/imap/fetch.go:178 +0xd7
created by git.sr.ht/~rjarry/aerc/worker/imap.(*IMAPWorker).handleFetchMessages
worker/imap/fetch.go:172 +0x12b
This happens because the flags are updated in the callback that receives
the message itself. It causes the flag update to arrive in the same
channel/request. Ignore the messages that have an empty body (i.e. only
containing flag updates). This is inherently racy but there seems no way
to get rid of these extra messages.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Tested-by: Connor Kuehl <cipkuehl@gmail.com>
Since panics still regularly "destroy" the terminal, it is hard to get a
stack trace for panics you do not anticipate. This commit adds a panic
handler that automatically creates a logfile inside the current working
directory.
It has to be added to every goroutine that is started and will repair
the terminal on a panic.
Signed-off-by: Moritz Poldrack <git@moritz.sh>
Acked-by: Robin Jarry <robin@jarry.cc>
This commit fixes all occurrences of the abovementioned lint-error in
the codebase.
Signed-off-by: Moritz Poldrack <git@moritz.sh>
Acked-by: Robin Jarry <robin@jarry.cc>
Some messages contain invalid headers, textproto.ReadHeader fails with
an error:
malformed MIME header key: From nobody Fri Jan 14 19
And all other messages of the selected folder are ignored.
Return an explicit error message to the UI and continue processing other
messages. This is consistent with what the maildir worker does.
Signed-off-by: Robin Jarry <robin@jarry.cc>
- Add maildir flags to complement a messages imap flags
- Set the "seen" flag on sent messages when using the maildir backend
- Cleanup AppendMessage interface to use models.Flag for both IMAP and
maildir
Before, we were using several IMAP-specific concepts to represent
information being displayed in the UI. Factor these structures out of
the IMAP package to make it easier for other backends to provide the
required information.
A sequence-set is an IMAP-specific implementation detail. Throughout the
UI, aerc simply operates using lists of opaque identifiers. In order to
loosen the coupling between the UI and IMAP in particular, replace most
usages of imap.SeqSet with []uint32, leaving the translation to a SeqSet
to the IMAP backend as needed.
Before, the information needed to display different parts of the UI was
tightly coupled to the specific messages being sent back and forth to
the backend worker. Separating out a models package allows us to be more
specific about exactly what a backend is able to and required to
provide for the UI.
Unfortunately, the IMAP protocol hasn't been designed to be used from multiple
goroutines at the same time. For instance, if you fetch twice the same message
from two different goroutines, it's not possible to tell whether the response
is for one receiver or the other. For this reason, go-imap clients aren't safe
to use from multiple goroutines.
This commit changes the IMAP workers to be synchronous again (a command is
executed only after the previous one has completed). To use IMAP from different
threads, popular clients (e.g. Thunderbird) typically open multiple
connections.