Some emails contain malformed MIME parts with invalid content
disposition headers or broken nested structures. Previously,
ParseEntityStructure would fail the entire parsing operation when
encountering such issues, making the whole message unreadable.
Instead of returning an error, log a warning and continue parsing
the remaining parts. This allows users to still read the valid
portions of a message even when some parts are malformed. Only
propagate MultipartError which indicates a fundamental structural
issue that cannot be recovered from.
Signed-off-by: Robin Jarry <robin@jarry.cc>
I am using a split for my GMail account, and can super easily get the
UI to be unresponsive by aggressively scrolling down the message list.
The problem is that if FetchBodyPart's context gets cancelled in the
middle of processing, the UI will never get a MessageInfo, and be stuck
forever.
This patch makes sure that the context we pass to FetchBodyPart is not
cancellable.
Signed-off-by: Simon Martin <simon@nasilyan.com>
Acked-by: Robin Jarry <robin@jarry.cc>
The deferred w.w.Close() caused the underlying writer to always be
closed twice: once via defer and again with the explicit return at
the end. For the SMTP sender this results in calling Quit() on an
already-closed connection.
Remove the defer and instead call w.w.Close() explicitly in each
error path so that the connection is closed exactly once.
Also fix a write error that was silently discarded (return nil instead
of return err).
Fixes: cb1c4c9c62 ("send: ensure crlf line endings")
Signed-off-by: Robin Jarry <robin@jarry.cc>
Tested-by: septante <septante@memeware.net>
Acked-by: septante <septante@memeware.net>
newSmtpSender() returned the io.WriteCloser from conn.Data() instead of
the smtpSender wrapper. Callers would only close the data writer without
ever calling Quit() or Close() on the underlying SMTP connection,
leaking it.
Return the smtpSender object so that its Close() method properly
terminates the SMTP session. Also send QUIT before closing since some
servers hang when the client disconnects without it. If Quit() fails,
fall back to a raw Close().
Fixes: 67923707ff ("Refactor send command")
Signed-off-by: Robin Jarry <robin@jarry.cc>
Tested-by: septante <septante@memeware.net>
Acked-by: septante <septante@memeware.net>
Rename parseEnvelope to ParseEnvelope to make it available for use by
other packages. The cache worker will need this function to reconstruct
envelope data from stored headers.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Reviewed-by: Simon Martin <simon@nasilyan.com>
Add an Index field to MessageInfo that indicates the message's position
in the current view. This allows workers to send incremental updates
with position information, enabling the UI to insert new messages at the
correct location without requiring a full refresh.
The JMAP push handler now includes index information when sending
unsolicited message updates, allowing the message list to stay
synchronized with server-side changes in real time.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Reviewed-by: Simon Martin <simon@nasilyan.com>
Add Directory and Filter fields to DirectoryContents, DirectoryThreaded,
SearchResults, MessageInfo, and FullMessage response types. Workers now
populate these fields so the UI knows which directory and filter context
each response belongs to.
This metadata allows the message store to correctly associate incoming
messages with their source context, which is essential for the offline
worker to cache messages by directory and for proper handling of
concurrent operations across multiple folders.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Reviewed-by: Simon Martin <simon@nasilyan.com>
Previously, worker action messages did not specify which directory they
targeted. Workers implicitly assumed operations applied to the currently
selected folder. This assumption breaks when the UI needs to perform
operations on a folder different from the one currently open, such as
when fetching messages from a background account or handling move/copy
operations across folders.
Add Directory field to FetchDirectoryContents, FetchDirectoryThreaded,
SearchDirectory, FetchMessageHeaders, FetchFullMessages,
FetchMessageBodyPart, FetchMessageFlags, DeleteMessages, FlagMessages,
AnsweredMessages, and ForwardedMessages. Add Source field to
CopyMessages and MoveMessages to specify the origin folder.
The message store now populates these fields with its directory name.
Each worker backend validates the directory and switches context when
necessary. For IMAP, this means issuing a SELECT command when the
requested directory differs from the currently selected mailbox.
Update the foldermapper middleware to translate the new Directory and
Source fields between external and internal folder names in both
ProcessAction and PostMessage.
In the maildir backend, pass the resolved directory to search and
msgInfoFromUid instead of relying on the selected folder state. This
ensures FetchMessageHeaders and SearchDirectory operate on the correct
directory when it differs from the currently selected one.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Reviewed-by: Simon Martin <simon@nasilyan.com>
Move the cancellation context from individual message types to the base
Message struct. Previously, only a few message types (OpenDirectory,
FetchDirectoryContents, FetchDirectoryThreaded, SearchDirectory,
FetchMessageHeaders, FetchMessageFlags) had a Context field, requiring
special handling in workers.
Now all worker messages carry a context through the base Message type,
set via PostAction's new first parameter. Workers access it uniformly
via the Context() method which returns context.Background() when unset.
Add a Close() method to MessageStoreView that cancels pending fetch
operations when the message viewer is closed, preventing wasted work on
messages the user is no longer viewing.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Reviewed-by: Simon Martin <simon@nasilyan.com>
SASL and OAuth2 authentication logic was duplicated across the IMAP
worker and the SMTP sender code. Both implementations handled XOAUTH2,
OAUTHBEARER, and token caching independently, making maintenance
difficult and bug fixes error-prone.
Move this shared logic into lib/auth where it can be reused by all
backends. The IMAP worker no longer needs its own OAuth structs and
configuration parsing since it can now rely on the common
implementation also used by the SMTP sender.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Reviewed-by: Simon Martin <simon@nasilyan.com>
The lib/auth package parses Authentication-Results email headers, not
general authentication mechanisms. Rename it to lib/authres to better
reflect its purpose and avoid confusion with SASL/OAuth authentication
code. This also aligns with the go-msgauth/authres library it wraps.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Reviewed-by: Simon Martin <simon@nasilyan.com>
This is the result of the following command:
go run golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest -fix ./...
Signed-off-by: Robin Jarry <robin@jarry.cc>
Add a new html-inline-images option in the [viewer] section that enables
inlining of images referenced by <img> tags with cid: URLs in HTML emails.
When enabled, aerc will parse HTML content to find <img src="cid:...">
references, fetch the corresponding image parts using their Content-ID
and use base64 encoding to embed images directly in HTML using data:
URLs.
This allows HTML emails with embedded images to be properly viewed in
w3m and other browsers that support data: URLs. The implementation uses
asynchronous callbacks to fetch all images in parallel without blocking.
The feature works with all aerc commands that fetch message parts (:save,
:open, :pipe, and viewing). Updated the filters/html script to enable
w3m image support with sixel graphics when img2sixel is available.
Add documentation for the new html-inline-images viewer option in both
the default aerc.conf and the aerc-config(5) man page.
Implements: https://todo.sr.ht/~rjarry/aerc/252
Changelog-added: New `[viewer].html-inline-images` option to replace
`<img src="cid:...">` tags in `text/html` parts with their related
`image/*` part data encoded in base64. For this to work with sixel
compatible terminals, you need to update your filters with `text/html =
! html -sixel` and install `img2sixel`.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Tested-by: Bence Ferdinandy <bence@ferdinandy.com>
Tested-by: Inwit <inwit@sindominio.net>
Tested-by: Matthew Phillips <matthew@matthewphillips.info>
In order to load message parts for rendering html emails, we need to
know the parts Content-ID header values (if any). Add this field to the
BodyStructure type and initialize it in all workers.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Tested-by: Bence Ferdinandy <bence@ferdinandy.com>
Tested-by: Inwit <inwit@sindominio.net>
Tested-by: Matthew Phillips <matthew@matthewphillips.info>
As reported by bonswouar on IRC, the mail received hook is not invoked
when a new mail is received on an account that's not currently active,
only when that account is activated.
This is due to a bug in MessageStore.Update, that wrongly assumes that
messages in a directory are displayed in increasing UID order - this is
only the case when reverse-msglist-order (that defaults to false) is
true. So unless there's less than one page worth of messages in a
directory or reverse-msglist-order is true, MessageStore.Update will
think that new messages are outside of the current page, and not fetch
their headers (this is what might trigger the mail received hook).
This patch ensures that the order is properly taken into account, and
makes the mail received hook be invoked whether the account is active or
not.
Signed-off-by: Simon Martin <simon@nasilyan.com>
Acked-by: Robin Jarry <robin@jarry.cc>
Add a configuration option to specify a directory where all temporary
files are saved.
Changelog-added: A custom directory can now be specified for temporary
files.
Signed-off-by: inwit <inwit@sindominio.net>
Acked-by: Robin Jarry <robin@jarry.cc>
If you start aerc with mouse mode activated and you happen to touch the
mouse, you might encounter this crash [1].
The problem is that if the grid has not been drawn yet, reflow has not
been called, hence {row,column}Layout is nil, and we crash accessing
their elements.
There might be ways to better synchronize things (?), but the easiest is
to simply ignore mouse events if {row,column}Layout is nil; this is what
this patch does.
[1] panic: runtime error: slice bounds out of range [:2] with capacity 0
goroutine 1 [running]:
git.sr.ht/~rjarry/aerc/lib/log.PanicHandler()
git.sr.ht/~rjarry/aerc/lib/log/panic-logger.go:51 +0x6cf
panic({0xc8c660?, 0xc001914000?})
runtime/panic.go:792 +0x132
git.sr.ht/~rjarry/aerc/lib/ui.(*Grid).MouseEvent(0xc000162090, 0x63, 0x16, {0xc9ac60?, 0xc000414440?})
git.sr.ht/~rjarry/aerc/lib/ui/grid.go:144 +0x5a6
git.sr.ht/~rjarry/aerc/app.(*AccountView).MouseEvent(0x142db50?, 0xcd1640?, 0xc0000478f0?, {0xc9ac60?, 0xc000414440?})
git.sr.ht/~rjarry/aerc/app/account.go:192 +0x25
[...]
Signed-off-by: Simon Martin <simon@nasilyan.com>
Acked-by: Robin Jarry <robin@jarry.cc>
When reloading the configuration with :reload, global variables in the
config package are reset to their startup values and then, the config is
parsed from disk. While the parsing is done, these variables are
temporarily in an inconsistent and possibly invalid state.
When commands are executed interactively from aerc, they are handled by
the main goroutine which also deals with UI rendering. No UI render will
be done while :reload is in progress.
However, the IPC socket handler runs in an independent goroutine. This
has the unfortunate side effect to let the UI goroutine to run while
config parsing is in progress and causes crashes:
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x6bb142]
goroutine 1 [running]:
git.sr.ht/~rjarry/aerc/lib/log.PanicHandler()
lib/log/panic-logger.go:51 +0x6cf
panic({0xc1d960?, 0x134a6e0?})
/usr/lib/go/src/runtime/panic.go:783 +0x132
git.sr.ht/~rjarry/aerc/config.(*StyleConf).getStyle(0xc00038b908?, 0x4206b7?)
config/style.go:386 +0x42
git.sr.ht/~rjarry/aerc/config.StyleSet.Get({0x0, 0x0, 0x0, {0x0, 0x0, 0x0}}, 0x421a65?, 0x0)
config/style.go:408 +0x8b
git.sr.ht/~rjarry/aerc/config.(*UIConfig).GetStyle(...)
config/ui.go:379
git.sr.ht/~rjarry/aerc/lib/ui.(*TabStrip).Draw(0xc000314700, 0xc000192230)
lib/ui/tab.go:378 +0x15b
git.sr.ht/~rjarry/aerc/lib/ui.(*Grid).Draw(0xc000186fc0, 0xc0002c25f0)
lib/ui/grid.go:126 +0x28e
git.sr.ht/~rjarry/aerc/app.(*Aerc).Draw(0x14b9f00, 0xc0002c25f0)
app/aerc.go:192 +0x1fe
git.sr.ht/~rjarry/aerc/lib/ui.Render()
lib/ui/ui.go:155 +0x16b
main.main()
main.go:310 +0x997
Make the reload operation safe by changing how config objects are
exposed and updated. Change all objects to be atomic pointers. Expose
public functions to access their value atomically. Only update their
value after a complete and successful config parse. This way the UI
thread will always have access to a valid configuration.
NB: The account configuration is not included in this change since it
cannot be reloaded.
Fixes: https://todo.sr.ht/~rjarry/aerc/319
Reported-by: Anachron <gith@cron.world>
Signed-off-by: Robin Jarry <robin@jarry.cc>
The maintainer of this library has gone AWOL. We are depending on
a patch that has never been merged. Let's vendor the library to avoid
future issues.
This patch has been made with the following steps:
git clone https://github.com/konimarti/jwz lib/jwz
git -C lib/jwz checkout fix-missing-messages
mv lib/jwz/test/testdata/ham lib/jwz/testdata
sed -i 's#test/testdata#testdata#' lib/jwz/jwz_test.go
rm -rf lib/jwz/.* lib/jwz/docs lib/jwz/examples lib/jwz/test
sed -i 's#github.com/gatherstars-com/jwz#git.sr.ht/~rjarry/aerc/lib/jwz#' \
lib/threadbuilder.go
go mod tidy
git add --intent-to-add lib/jwz
make fmt
Along with some manual adjustments to fix the linter warnings. Also, to
make the patch smaller, I only kept 93 test emails from the test data
fixture.
Changelog-changed: The JWZ library used for threading is now vendored.
Signed-off-by: Robin Jarry <robin@jarry.cc>
Reviewed-by: Moritz Poldrack <moritz@poldrack.dev>
Go has evolved significantly over the years and has introduced some
handy helper functions that make the code easier to read.
Use helper functions like slices.Contains, map.Copy, and
strings.CutPrefix, when appropriate.
Signed-off-by: Moritz Poldrack <git@moritz.sh>
Acked-by: Robin Jarry <robin@jarry.cc>
The various handleFetchXYZ functions do not consistently request
X-GM-LABELS when interacting with GMail, and the in-memory "label state"
can easily get out-of-sync with the server.
This patch fixes this, and also ensures that we only log that we're
attaching a label to a message when we actually do (and if so, puts the
account name in the log line).
Signed-off-by: Simon Martin <simon@nasilyan.com>
Acked-by: Robin Jarry <robin@jarry.cc>
When typing inside textinput, Ctrl+Left places the cursor at the start
of the current word, and Ctrl+Right goes to the start of the next one.
Ctrl+Left uses the same code `deleteWord()` function used (which is now
split into a separate function), and Ctrl+Right uses similar lookup in
the other direction.
Implements: https://todo.sr.ht/~rjarry/aerc/231
Signed-off-by: GregTheMadMonk <yagreg7@gmail.com>
Tested-by: skejg <grolleman@zoho.com>
Acked-by: Robin Jarry <robin@jarry.cc>
Use the GMail IMAP extension to fetch labels for messages when
use-gmail-ext is enabled. Labels are automatically fetched when fetching
headers for a message and when fetching flags for a message. They should
be correctly cached.
Link: https://developers.google.com/workspace/gmail/imap/imap-extensions
Changelog-added: Readonly support for GMail labels.
Signed-off-by: Jan Černohorský <jan@grsc.cz>
Tested-by: Vojtěch Káně <vojta001@vkane.cz>
Acked-by: Robin Jarry <robin@jarry.cc>
My kids' school has put a leading space in their name, which breaks the
visual alignment in the message list and viewer.
This patch calls TrimSpace when displaying addresses and fixes both
views.
Signed-off-by: Simon Martin <simon@nasilyan.com>
Acked-by: Robin Jarry <robin@jarry.cc>
When navigating between messages in the Message Viewer, the whole tab is
updated except its title: it remains set to the subject of the message
that was initially opened in that tab.
Tabs.Replace needs to update both the content *and* the title; this is
what this patch does.
Signed-off-by: Simon Martin <simon@nasilyan.com>
Acked-by: Robin Jarry <robin@jarry.cc>
Some address names may be quoted AND partially encoded. That is only
a word in the middle of the name could be encoded (and be surrounded by
=? and ?= delimiters).
Just check if the address name contains these two delimiters (regardless
of their position) to determine if we should re-decode the name.
Fixes: 44b14ebdff ("rfc822: be liberal with invalid address headers")
Signed-off-by: Robin Jarry <robin@jarry.cc>
Add a template for the terminal tab title and define two
terminal-specific properties: Title which expands to whatever the
underlying application requests via OSC and defaults to the command name
and Bell which is a boolean indicating whether the application has
emitted bell, the flag is cleared when the corresponding tab gains focus
and is not set at all if it currently has it.
This can among other things be used to highlight the terminal tab when
the underlying process prints the bell, such as by setting the template
as such:
tab-title-terminal={{if .Bell}}[BELL] {{end}}{{.Title}}
Implements: https://todo.sr.ht/~rjarry/aerc/138
Changelog-added: A tab-title-terminal setting for customizing the
title of tabs in which the terminal widget is run.
Requested-by: Drew DeVault <drew@ddevault.org>
Signed-off-by: Karel Balej <balejk@matfyz.cz>
Acked-by: Robin Jarry <robin@jarry.cc>
I noticed that the fix I made via b576ab28 (and the general idea to get
rid of materialized RUE counters) does not work, because we're extremely
unlikely to have all the messages' flags in memory. So the patch worked
"by chance" when updating messages that are in the active message list
(e.g. their headers are already fetched) but not for "out of view"
messages.
This alternative patch "just" captures unsolicited MessageUpdate
messages from the server, and updates the Unseen counter according to
the status reported by the server for that message. This allows to
synchronize our view with the server's.
Fixes: b576ab28 ("properly update UI upon message (un)read in [...]")
Fixes: https://todo.sr.ht/~rjarry/aerc/307
Signed-off-by: Simon Martin <simon@nasilyan.com>
Tested-by: Karel Balej <balejk@matfyz.cz>
Acked-by: Robin Jarry <robin@jarry.cc>
Add a flag to indicate whether the account has received new messages
since it last had focus and allow its use in the templates.
Changelog-added: A `.HasNew` flag indicating whether the account has
received new messages to be used in the templates.
Signed-off-by: Karel Balej <balejk@matfyz.cz>
Tested-by: Inwit <inwit@sindominio.net>
Acked-by: Robin Jarry <robin@jarry.cc>
When sort-thread-siblings = false and threading-by-subject = true, do
not order siblings by ascending UID. Instead, use the message Subject
headers to order siblings.
Changelog-changed: Thread siblings will now be ordered by subject if
`[ui].sort-thread-siblings = false` and `[ui].threading-by-subject
= true`.
Reported-by: Brendan Jackman <jackmanb@google.com>
Signed-off-by: Robin Jarry <robin@jarry.cc>
Tested-by: Brendan Jackman <jackmanb@google.com>
So far, the :tag command in the notmuch and jmap workers allows adding a
tag (by prefixing it with '+') and removing it (by prefixing it with
'-'). Add a new functionality to this command, allowing it to toggle a
tag by prefixing it with '!'.
Fixes: https://todo.sr.ht/~rjarry/aerc/292
Changelog-added: It is now possible to toggle notmuch and JMAP tags.
Signed-off-by: inwit <inwit@sindominio.net>
Acked-by: Robin Jarry <robin@jarry.cc>
After "gmake config", git-send-email currently fails on Darwin with the
following:
> lib/watchers/fsevents.go:67:11: Error return value of `w.w.Start` is
> not checked (errcheck)
This patch fixes this by checking the return code and bubbling it up in
case of error.
Signed-off-by: Simon Martin <simon@nasilyan.com>
Acked-by: Robin Jarry <robin@jarry.cc>
This patch fixes an error that occurs when decrypting an email with an
invalid gpg signature. The error "gpg-exit 33554433" will be thrown and
the email will not be opened.
Signed-off-by: Bryce Vandegrift <bryce@brycevandegrift.xyz>
Acked-by: Robin Jarry <robin@jarry.cc>
Due to vaxis low-level design, the enter key of the number block has
a different key code that a regular carriage return. Since most people
do not care which of the two they are hitting, remap the former and
up/down/left/right to act like the latter.
Changelog-added: Support for number block enter to act as <Cr>
in mappings.
Signed-off-by: Moritz Poldrack <git@moritz.sh>
Reviewed-by: Tim Culverhouse <tim@timculverhouse.com>
Acked-by: Robin Jarry <robin@jarry.cc>
During startup, d.account and d.folder are not set and thus not
lead to a segmentation fault when running a signature command.
Only set these values if they are available.
Fixes: da5ca1a5 ("template: pass account and folder names to signature command")
Changelog-fixed: Fix an error when starting aerc through a `mailto:`
link when the `new_message` template runs a signature command.
Signed-off-by: Moritz Poldrack <git@moritz.sh>
Acked-by: Robin Jarry <robin@jarry.cc>
In some cases it can be desirable to receive a success notification for
a message from the server. This currently only works on SMTP.
Changelog-added: Add option to request full DSN for SMTP sent messages.
Signed-off-by: Moritz Poldrack <git@moritz.sh>
Acked-by: Robin Jarry <robin@jarry.cc>
lib/marker makes extensive uses of maps, but does not guard against
concurrent accesses. Add two mutexes to ensure read and write access do
not happen simultaneously.
Changelog-fixed: Fixed an unguarded concurrent map access leading
to crashes.
Signed-off-by: Moritz Poldrack <git@moritz.sh>
Tested-by: Terrance <srht@terrance.allofti.me>
This commit fixes a critical bug found by the CI where import was
allegedly misspelled
Signed-off-by: Moritz Poldrack <git@moritz.sh>
Acked-by: Robin Jarry <robin@jarry.cc>
Add a direct mailserver poker that tries to connect to the mailserver
with the highest priority retrieved from via DNS.
Signed-off-by: Moritz Poldrack <git@moritz.sh>
Acked-by: Robin Jarry <robin@jarry.cc>
Add a fallback to the autoconfig module that performs some educated
guesses regarding hostname, port, and encryption.
Signed-off-by: Moritz Poldrack <git@moritz.sh>
Acked-by: Robin Jarry <robin@jarry.cc>
Add the capability to parse DNS entries to infer mailserver settings.
Signed-off-by: Moritz Poldrack <git@moritz.sh>
Acked-by: Robin Jarry <robin@jarry.cc>
Add a function to retrieve configurations from the autoconfig-subdomain
or a .well-known file.
Signed-off-by: Moritz Poldrack <git@moritz.sh>
Acked-by: Robin Jarry <robin@jarry.cc>
Add a command to copy links to the system clipboard. This can be useful
for instance when the URL should be handled in some other way than
opened (e. g. resent via some other communication channel or saved to a
file). It also improves security for situations where it's not desirable
for the URL to be visible on the process command line, such as when it
contains sensitive information such as an access token.
Adapted from open-link.
Changelog-added: If supported by the terminal, links from a message
can now be copied to the system clipboard with the :copy-link command
of the message viewer.
Signed-off-by: Karel Balej <balejk@matfyz.cz>
Acked-by: Robin Jarry <robin@jarry.cc>
Make sure that links placed verbatim inside HTML elements' bodies are
not parsed along with adjacent HTML tags as illustrated in the new test
case.
Also change the existing code to use the idiomatic Go way to get a
set-like functionality.
Changelog-fixed: Parsed links in HTML message parts now do not include
trailing HTML tags.
Signed-off-by: Karel Balej <balejk@matfyz.cz>
Tested-by: Jakub Růžička <jakub.ruzicka@matfyz.cz>