Commit graph

260 commits

Author SHA1 Message Date
Mechiel Lukkien
a4c6fe815f
make some maintenance commands that were previously unlisted listed
we refer to these commands in output of "mox verifydata", so they should be
findable other than through the code...
2023-08-10 12:29:46 +02:00
Mechiel Lukkien
7cceb3d834
add comment about not verifying Sender for submissions 2023-08-10 12:18:05 +02:00
Mechiel Lukkien
6b68920a3a
Go's LookupAddr will return non-absolute names, seemingly for single-label names from /etc/hosts, turn them into absolute names so our verifying forward lookups can succeed 2023-08-10 11:52:35 +02:00
Mechiel Lukkien
a30d8c1378
for localserve, don't special-case smtp submit
the recent webmail addition added localserve local delivery in queue.Add, so we
just that for smtpserver too.

and don't drop incoming smtp deliver messages, but deliver as normal.
2023-08-10 11:28:57 +02:00
Mechiel Lukkien
ce91b7d23e
update roadmap 2023-08-10 11:05:38 +02:00
Mechiel Lukkien
0434e49c3a
webmail: while attachment viewer is open, don't handle global keyboard shortcuts (like search, going to inbox)
feedback from jonathan, thanks!
2023-08-10 11:02:13 +02:00
Mechiel Lukkien
c24bb063e5
webmail tweaks
- padding on small attachment download button.
- don't remember "show html" but always display text first.
- propagate modseq to message when flags/keywords change, so "show internals" shows the update.
2023-08-10 10:56:04 +02:00
Mechiel Lukkien
f48a53726e
when clearing search, open inbox
feedback from jonathan, thanks!
2023-08-10 10:42:54 +02:00
Mechiel Lukkien
038b478d16
listen/bind in deterministic order for consistent error messages, and warn if quickstart cannot find public ip's
without public ip's, the generated mox config will try to listen on 0.0.0.0 and
::, but because there is already a listener for 127.0.0.1:80 (and possibly
others), a bind for 0.0.0.0:80 will fail. explicit public ip's are needed.

the public http listener is useful for ACME validation over http.

for issue #52
2023-08-10 10:29:06 +02:00
Mechiel Lukkien
01bcd98a42
add flag to ruleset that indicates a message is forwarded, slightly modifying how junk analysis is done
part of PR #50 by bobobo1618
2023-08-09 22:31:37 +02:00
Mechiel Lukkien
9c31789c56
add option to ruleset to accept incoming spammy messages to a configured mailbox
this is based on @bobobo1618's PR #50. bobobo1618 had the right idea, i tried
including an "is forwarded email" configuration option but that indeed became
too tightly coupled. the "is forwarded" option is still planned, but it is
separate from the "accept rejects to mailbox" config option, because one could
still want to push back on forwarded spam messages.

we do an actual accept, delivering to a configured mailbox, instead of storing
to the rejects mailbox where messages can automatically be removed from.  one
of the goals of mox is not pretend to accept email while actually junking it.
users can still configure delivery to a junk folder (as was already possible),
but aren't deleted automatically. there is still an X-Mox-Reason header in the
message, and a log line about accepting the reject, but otherwise it is
registered and treated as an (smtp) accept.

the ruleset mailbox is still required to keep that explicit. users can specify
Inbox again.

hope this is good enough for PR #50, otherwise we'll change it.
2023-08-09 22:25:10 +02:00
Mechiel Lukkien
383fe4f53a
explicitly store in a Message whether it was delivered to the rejects mailbox
soon, we can have multiple rejects mailboxes.  and checking against the
configured rejects mailbox name wasn't foolproof to begin with, because it may
have changed between delivery to the rejects mailbox and the message being
moved.

after upgrading, messages currently in rejects mailboxes don't have IsReject
set, so they don't get the special rejecs treatment when being moved. they are
removed from the rejects mailbox after some time though, and newly added
rejects will be treated correctly. so this means some existing messages wrongly
delivered to the rejects mailbox, and moved out, aren't used (for a positive
signal) for future deliveries.  saves a bit of complexity in the
implementation.  i think the tradeoff is worth it.

related to discussion in issue #50
2023-08-09 16:52:24 +02:00
Mechiel Lukkien
0fc59af9a8
add Deliver-To header for delivered messages
for (experimental) rfc 9228
2023-08-09 10:20:45 +02:00
Mechiel Lukkien
20ebdae8ea
in webmail, automatically mark message as nonjunk when open for 5 seconds, and prevent extraneous newlines when composing a reply to selected text 2023-08-09 09:45:54 +02:00
Mechiel Lukkien
34ede1075d
remove last remnants of treating a mailbox named "Sent" specially, in favor of special-use mailbox flags
a few places still looked at the name "Sent". but since we have special-use
flags, we should always look at those. this also changes the config so admins
can specify different names for the special-use mailboxes to create for new
accounts, e.g. in a different language. the old config option is still
understood, just deprecated.
2023-08-09 09:31:23 +02:00
Mechiel Lukkien
19b819d222
in smtpserver, don't put unrecognized smtp commands in prometheus metrics
can blow up prometheus storage.
2023-08-09 08:12:59 +02:00
Mechiel Lukkien
f5af258075
in account & admin web api's, differentiate between server errors and user errors, and add a prometheus monitoring rule for server errors 2023-08-09 08:02:58 +02:00
Mechiel Lukkien
8c3c12d96a
add message size consistency check
the bulk of a message is stored on disk. a message prefix is stored in the
database (for prefixed headers like "Received:"). this adds a check to ensure
Size = prefix length + on-disk file size.

verifydata also checks for this now.

and one older and one new (since yesterday) bug was found. the first when
appending a message without a header/body section (uncommon). the second when
sending messages from webmail with localserve (uncommon).
2023-08-08 22:10:53 +02:00
Mechiel Lukkien
49cf16d3f2
fix race in test setup/teardown
not easily triggered, but it happened just now on a build server.
2023-08-07 23:14:31 +02:00
Mechiel Lukkien
849b4ec9e9
add webmail
it was far down on the roadmap, but implemented earlier, because it's
interesting, and to help prepare for a jmap implementation. for jmap we need to
implement more client-like functionality than with just imap. internal data
structures need to change. jmap has lots of other requirements, so it's already
a big project. by implementing a webmail now, some of the required data
structure changes become clear and can be made now, so the later jmap
implementation can do things similarly to the webmail code. the webmail
frontend and webmail are written together, making their interface/api much
smaller and simpler than jmap.

one of the internal changes is that we now keep track of per-mailbox
total/unread/unseen/deleted message counts and mailbox sizes.  keeping this
data consistent after any change to the stored messages (through the code base)
is tricky, so mox now has a consistency check that verifies the counts are
correct, which runs only during tests, each time an internal account reference
is closed. we have a few more internal "changes" that are propagated for the
webmail frontend (that imap doesn't have a way to propagate on a connection),
like changes to the special-use flags on mailboxes, and used keywords in a
mailbox. more changes that will be required have revealed themselves while
implementing the webmail, and will be implemented next.

the webmail user interface is modeled after the mail clients i use or have
used: thunderbird, macos mail, mutt; and webmails i normally only use for
testing: gmail, proton, yahoo, outlook. a somewhat technical user is assumed,
but still the goal is to make this webmail client easy to use for everyone. the
user interface looks like most other mail clients: a list of mailboxes, a
search bar, a message list view, and message details. there is a top/bottom and
a left/right layout for the list/message view, default is automatic based on
screen size. the panes can be resized by the user. buttons for actions are just
text, not icons. clicking a button briefly shows the shortcut for the action in
the bottom right, helping with learning to operate quickly. any text that is
underdotted has a title attribute that causes more information to be displayed,
e.g. what a button does or a field is about. to highlight potential phishing
attempts, any text (anywhere in the webclient) that switches unicode "blocks"
(a rough approximation to (language) scripts) within a word is underlined
orange. multiple messages can be selected with familiar ui interaction:
clicking while holding control and/or shift keys.  keyboard navigation works
with arrows/page up/down and home/end keys, and also with a few basic vi-like
keys for list/message navigation. we prefer showing the text instead of
html (with inlined images only) version of a message. html messages are shown
in an iframe served from an endpoint with CSP headers to prevent dangerous
resources (scripts, external images) from being loaded. the html is also
sanitized, with javascript removed. a user can choose to load external
resources (e.g. images for tracking purposes).

the frontend is just (strict) typescript, no external frameworks. all
incoming/outgoing data is typechecked, both the api request parameters and
response types, and the data coming in over SSE. the types and checking code
are generated with sherpats, which uses the api definitions generated by
sherpadoc based on the Go code. so types from the backend are automatically
propagated to the frontend.  since there is no framework to automatically
propagate properties and rerender components, changes coming in over the SSE
connection are propagated explicitly with regular function calls.  the ui is
separated into "views", each with a "root" dom element that is added to the
visible document. these views have additional functions for getting changes
propagated, often resulting in the view updating its (internal) ui state (dom).
we keep the frontend compilation simple, it's just a few typescript files that
get compiled (combined and types stripped) into a single js file, no additional
runtime code needed or complicated build processes used.  the webmail is served
is served from a compressed, cachable html file that includes style and the
javascript, currently just over 225kb uncompressed, under 60kb compressed (not
minified, including comments). we include the generated js files in the
repository, to keep Go's easily buildable self-contained binaries.

authentication is basic http, as with the account and admin pages. most data
comes in over one long-term SSE connection to the backend. api requests signal
which mailbox/search/messages are requested over the SSE connection. fetching
individual messages, and making changes, are done through api calls. the
operations are similar to imap, so some code has been moved from package
imapserver to package store. the future jmap implementation will benefit from
these changes too. more functionality will probably be moved to the store
package in the future.

the quickstart enables webmail on the internal listener by default (for new
installs). users can enable it on the public listener if they want to. mox
localserve enables it too. to enable webmail on existing installs, add settings
like the following to the listeners in mox.conf, similar to AccountHTTP(S):

	WebmailHTTP:
		Enabled: true
	WebmailHTTPS:
		Enabled: true

special thanks to liesbeth, gerben, andrii for early user feedback.

there is plenty still to do, see the list at the top of webmail/webmail.ts.
feedback welcome as always.
2023-08-07 21:57:03 +02:00
Mechiel Lukkien
141637df43
when creating a mailbox subscription, don't just try to insert a record into the database and handle bstore.ErrUnique, the transaction will have been marked as botched
behaviour around failing DB calls that change data (insert/update) was changed
in bstore quite some time ago. the tx state in bstore would become inconsistent
when one or more (possibly unique) indexes had been modified, but then an
ErrUnique would occur for the next index. bstore doesn't know how to roll back
the partial changes during a transaction, so it marks the tx as botched and
refuses further operations. so, we cannot just try to insert, wait for a
possible ErrUnique, but then still try to continue with the transaction.
instead, we check if the record exists and only insert it if we couldn't find
it.

found while working on webmail.
2023-08-01 10:14:02 +02:00
Mechiel Lukkien
19550cc041
use Go's mail.ReadMessage instead of textproto.ReadMIMEHeaders and decode RFC 2047 charsets in subject header when parsing message
as the recent Go patch release showed, textproto.ReadMIMEHeaders is parsing
http headers, strictly. too strict for email message headers. valid headers,
e.g. with a slash in them, were rejected by textproto.ReadMIMEHeaders.

the functions in Go's mail package handle RFC 2047 charset-encoded words in
address headers. it can do that because we tell it those headers are addresses,
where such encodings are valid. but that encoding isn't valid in all places in
all headers. for other cases, we must decode explicitly, such as for the
subject header.

with this change, some messages that could not be parsed before can now be
parsed (where headers were previously rejected for being invalid). and the
subject of parsed messages could now be properly decoded. you could run "mox
ensureparsed -all <account>" (while mox isn't running) to force reparsing all
messages. mox needs a subcommand to reparse while running...

it wasn't much of a problem before, because imap email clients typically do
their own parsing (of headers, including subject decoding) again.  but with the
upcoming webmail client, any wrong parsing quickly reveals itself.
2023-08-01 09:50:26 +02:00
Mechiel Lukkien
3ef1f31359
update dependencies 2023-07-28 22:47:28 +02:00
Mechiel Lukkien
01adad62b2
implement decoding charsets (other than ascii and utf-8) while reading textual message parts, and improve search
message.Part now has a ReaderUTF8OrBinary() along with the existing Reader().
the new function returns a reader of decoded content. we now use it in a few
places, including search. we only support the charsets in
golang.org/x/text/encoding/ianaindex.

search has also been changed to not read the entire message in memory. instead,
we make one 8k buffer for reading and search in that, and we keep the buffer
around for all messages. saves quite some allocations when searching large
mailboxes.
2023-07-28 22:15:23 +02:00
Mechiel Lukkien
a31dfc573e
in smtpserver, allow a space after "mail from:" and "rcpt to:" commands for submission connections
the space is explicitly mentioned as not valid in rfc 5321, but it clients do
send it, such as microsoft outlook 365 apps for enterprise. no need to punish
such users, we'll allow it. but only for submission, not regular smtp, because
it is normally a sign of a spammer. we still don't allow it in pedantic mode
(as used by localserve).

for issue #51 by hmfaysal, thanks for reporting and testing!
2023-07-28 20:49:19 +02:00
Mechiel Lukkien
6273afe84f
fix building fresh docker images for integration tests
i always get bitten by some caching or missing checks when i use docker...
Dockerfile.moxmail doesn't exist anymore, but that doesn't matter, it doesn't
even look at it but will just use some image that is still around (based on the
name?) i suppose that means docker-compose also doesn't rebuild an image when
the dockerfile mentioned in the build changes.
2023-07-26 21:58:12 +02:00
Mechiel Lukkien
5be4e91979
new items on roadmap, mention delivered-to rfc, fix wording in comments 2023-07-26 19:23:20 +02:00
Mechiel Lukkien
a92784b824
add missing account close, for retraining junk filter for an account with the retrain cli command 2023-07-26 19:21:58 +02:00
Mechiel Lukkien
e3d0a3a001
fix bug with cli import command in case the mbox/maildir had keywords, future delivery to the mailbox would fail with duplicate uid's.
accounts with a mailbox with this problem can be fixed by running the "mox
fixuidmeta <account>" command.

we were resetting the mailbox uidnext after delivering messages when we were
setting new keywords on the mailbox at the end of the import. so in a future
delivery attempt to that mailbox, a uid would be chosen that was already
present.

the fix is to fetch the updated mailbox from the database before setting the
new keywords.

http/import.go doesn't have this bug because it was already fetching the
mailbox before updating keywords (because it can import into many mailboxes,
so different code).

the "mox verifydata" command (recommended with backups) also warns about this
issue (but doesn't fix it)

found while working on new functionality (webmail).
2023-07-26 10:09:36 +02:00
Mechiel Lukkien
700118dbd2
add Content-Type header to message delivered for new mox releases
at least the android gmail/mail app doesn't show messages without content-type
header. i believe missing content-type is meant to be interpreted as
text/plain, but doesn't hurt to be explicit.
2023-07-25 08:24:05 +02:00
Mechiel Lukkien
7f1b7198a8
add condstore & qresync imap extensions
for conditional storing and quick resynchronisation (not sure if mail clients are actually using it that).

each message now has a "modseq". it is increased for each change. with
condstore, imap clients can request changes since a certain modseq. that
already allows quickly finding changes since a previous connection. condstore
also allows storing (e.g. setting new message flags) only when the modseq of a
message hasn't changed.

qresync should make it fast for clients to get a full list of changed messages
for a mailbox, including removals.

we now also keep basic metadata of messages that have been removed (expunged).
just enough (uid, modseq) to tell client that the messages have been removed.
this does mean we have to be careful when querying messages from the database.
we must now often filter the expunged messages out.

we also keep "createseq", the modseq when a message was created. this will be
useful for the jmap implementation.
2023-07-24 21:25:50 +02:00
Mechiel Lukkien
cc4ecf2927
imap continuations must have a space after the "+"
prevented at least the gmail/mail (?) android app from appending a sent message
to the sent mailbox.
2023-07-24 19:54:55 +02:00
Mechiel Lukkien
bc62aae0e6
in imap4rev1 search, always send an untagged search response, also without matches
required by rfc. i noticed an example doing that in the condstore/qresync rfc.
2023-07-24 15:40:04 +02:00
Mechiel Lukkien
bca33c0364
don't recurse into error checking function xcheckf when sendmail fails
found when wanting to get rid of the only non-err "shadowing" warning.
2023-07-24 14:08:27 +02:00
Mechiel Lukkien
b7a0904907
cleanup for warnings by staticcheck
the warnings that remained were either unused code that i wanted to use in the
future, or other type's of todo's. i've been mentally ignoring them, assuming i
would get back to them soon enough to fix them. but that hasn't happened yet,
and it's better to have a clean list with only actual isses.
2023-07-24 13:55:36 +02:00
Mechiel Lukkien
8bc554b671
update roadmap, top items are likely to happen soon, add milter to the list (for issue #47) 2023-07-24 11:03:53 +02:00
Mechiel Lukkien
c0100f44e7
for test-upgrade, import a (hopefully large) mbox file, checking for performance/memory consumption
in the future, it would be good to actually start a mox and read
mailboxes/messages...
2023-07-24 11:00:11 +02:00
Mechiel Lukkien
840f3afb35
in domain dnscheck, also check for hostname of mail server resolving to a loopback ip
nowadays the quickstart will warn about this, but it may be missed/ignored. and
users that installed mox a few versions ago never got the warning. so now we
keep warning about it in the dns check.

based on feedback from Mendel on slack, thanks!
2023-07-24 09:23:41 +02:00
Mechiel Lukkien
2e5376d7eb
when moving/copying messages in imapserve, also ensure the message keywords make it into the destination mailbox keywords list 2023-07-24 08:49:19 +02:00
Mechiel Lukkien
f9e261e0fb
merge docker-compose-based quickstart and integration tests into a single integration test
the two were so similar it made sense to just have one that tests all. saves
building docker images.
2023-07-23 23:32:02 +02:00
Mechiel Lukkien
dcb0f0a82c
in DSNs, add a References header pointing to the message with deliverability issues
so mail user agents will show DSNs threaded/grouped with the original message.
we store the MessageID in the message queue, so we have the value within reach
when we need it.

i saw a references header in a DSN from gmail on a test account. makes sense to me.
2023-07-23 17:56:39 +02:00
Mechiel Lukkien
c5747bd656
go fmt and updated config after make build
for PR #49
2023-07-23 17:08:55 +02:00
bobobo1618
671fc5b8f1
Add a 'KeepRejects' option that disables auto-cleanup (#49)
Add a 'KeepRejects' option that disables auto cleanup of the rejects mailbox.
2023-07-23 17:03:09 +02:00
Mechiel Lukkien
e943e0c65d
fix delay with propagating mailbox changes to other imap (idle) connections
when broadcasting a change, we would try to send the changes on a channel,
non-blocking. if we couldn't send (because there was no pending blocked
receive), we would wait until the potential receiver would explicitly request
the changes. however, the imap idle handler would not explicitly request the
changes, but do a receive on the changes channel. since there was no pending
blocked send on the channel, that receive would block. only when another event
would come in, would both the pending and the new changes be sent.

we now use a channel only for signaling there are pending changes. the channel
is buffered, so when broadcasting we can just set the signal by a non-blocking
send and continue with the next listener. the receiver will get the buffered
signal. it can then get the changes directly, but lock-protected.

found when looking at a missing/delayed new message notification in thunderbird
when two messages arrive immediately after each other. this doesn't fix that
problem though: it seems thunderbird just ignores imap untagged "exists"
messages (indicating a new message arrived) during the "uid fetch" command that
it issued after notifications from an "idle" command.
2023-07-23 15:28:37 +02:00
Mechiel Lukkien
3e9b4107fd
move "link or copy" functionality to moxio
and add a bit more logging for unexpected failures when closing files.
and make tests pass with a TMPDIR on a different filesystem than the testdata directory.
2023-07-23 12:15:29 +02:00
Mechiel Lukkien
4a4d337ab4
improve comments 2023-07-23 09:42:29 +02:00
Mechiel Lukkien
70806137da
for submission over IPv6, allow missing "IPv6" tag in ip address (unless in pedantic mode)
an EHLO ipv4 address looks like this: "[1.2.3.4]". for ipv6, the syntax is:
"[IPv6🔡:1]". mail user agents aren't as careful in compliance as smtp
servers. for incoming messages from smtp servers, we want to be strict (we're
eager to find a reason not to accept spam messages, and not adhering to the
standards is usually a strong spam signal), but there is no reason to punish
authenticated users.

for the syntax requirements, see ABNF rule "address-literal" in rfc 5321.

for issue #48 by @bobobo1618, thanks!
2023-07-22 14:20:50 +02:00
Mechiel Lukkien
9c25c88542
reinstate go vet ./... 2023-07-22 14:02:05 +02:00
Mechiel Lukkien
5b17fcd712
print log line about unprivileged user after having initialized the values that are printed
we currently were logging as if we were starting with uid=0, which wasn't the case.
2023-07-19 11:32:19 +02:00
Mechiel Lukkien
17dac99830
fix spello and link to a working build on beta.gobuilds.org
if a window user visited beta.gobuilds.org, they would be redirected to the
windows build, which would fail. better point them to a working build that
shows links to the platform they may actually need.
2023-07-18 08:58:01 +02:00