Commit Graph

975 Commits

Author SHA1 Message Date
07533252b3 message: when parsing a message, don't treat absent header and empty header value the same
We now use "*string" for such header fields, for Content-* fields, as used in
the imapserver when responding to FETCH commands. We'll now return NIL for an
absent header, and "" (empty string) if the header value is empty.
2025-04-16 20:06:45 +02:00
3fe765dce9 imapserver: fix fuzz tests
The acc.Close() at the end of the fuzzing would find inconsistencies. For
example, message files on disk that aren't in the database file. I don't
understand what is happening there, the database file on disk does have those
messages, and it seems the database file is getting replaced. When running the
same code not as a fuzzing test but as a regular Go test doesn't show the
problem. So it seems to be some interaction with fuzzing. The problem is
"solved" (feels more like side-stepped), by starting each fuzz test with a
clean database. We still open & close the account in each fuzz test, and it
doesn't find consistency problems.
2025-04-16 11:21:01 +02:00
e7b562e3f2 imapclient: first step towards making package usable as imap client with other imap servers, and minor imapserver bug fix
The imapclient needs more changes, like more strict parsing, before it can be a
generally usable IMAP client, these are a few steps towards that.

- Fix a bug in the imapserver METADATA responses for TOOMANY and MAXSIZE.
- Split low-level IMAP protocol handling (new Proto type) from the higher-level
  client command handling (existing Conn type). The idea is that some simple
  uses of IMAP can get by with just using these commands, while more intricate
  uses of IMAP (like a synchronizing client that needs to talk to all kinds of
  servers with different behaviours and implemented extensions) can write custom
  commands and read untagged responses or command completion results
  explicitly. The lower-level method names have clearer names now, like
  ReadResponse instead of Response.
- Merge the untagged responses and (command completion) "Result" into a new
  type Response. Makes function signatures simpler. And make Response implement
  the error interface, and change command methods to return the Response as error
  if the result is NO or BAD. Simplifies error handling, and still provides the
  option to continue after a NO or BAD.
- Add UIDSearch/MSNSearch commands, with a custom "search program", so mostly
  to indicate these commands exist.
- More complete coverage of types for response codes, for easier handling.
- Automatically handle any ENABLED or CAPABILITY untagged response or response
  code for IMAP command methods on type Conn.
- Make difference between MSN vs UID versions of
  FETCH/STORE/SEARCH/COPY/MOVE/REPLACE commands more clear. The original MSN
  commands now have MSN prefixed to their name, so they are grouped together in
  the documentation.
- Document which capabilities are needed for a command.
2025-04-15 08:37:18 +02:00
2c1283f032 imapclient: clean up function signature of New, allowing for future options too 2025-04-11 21:04:13 +02:00
af3e9351bc imapserver: simplify and fix logic around processing changes while opening a mailbox (with SELECT or EXAMINE)
We were first getting UIDs in a transaction with a lock. Then getting the
changes and processing them in a special way. And then processing for qresync
in a new transaction. The special processing of changes is now gone, it seems
to have skipped adding/removing uids to the session, which can't be correct.
The new approach is just using a lock and transaction and process the whole
opening of the mailbox, and not processing any changes as part of the open, and
getting rid of the special "initial" mode processing a mailbox.
2025-04-11 20:28:35 +02:00
fd5167fdb3 imapserver: enable test that checked that an expunged message can still be read in sessions when they haven't processed the deletion yet.
We've been keeping track of references before we erase the message file for a
while now.
2025-04-11 18:27:42 +02:00
1a6d268e1d imapserver: check for UIDNEXT overflow when adding a message to a mailbox
Return an error, with instructions so a user may be able to work around the
issue.
2025-04-11 18:22:29 +02:00
507ca73b96 imapserver: implement UIDONLY extension, RFC 9586
Once clients enable this extension, commands can no longer refer to "message
sequence numbers" (MSNs), but can only refer to messages with UIDs. This means
both sides no longer have to carefully keep their sequence numbers in sync
(error-prone), and don't have to keep track of a mapping of sequence numbers to
UIDs (saves resources).

With UIDONLY enabled, all FETCH responses are replaced with UIDFETCH response.
2025-04-11 11:45:49 +02:00
8bab38eac4 imapserver: implement NOTIFY extension from RFC 5465
NOTIFY is like IDLE, but where IDLE watches just the selected mailbox, NOTIFY
can watch all mailboxes. With NOTIFY, a client can also ask a server to
immediately return configurable fetch attributes for new messages, e.g. a
message preview, certain header fields, or simply the entire message.

Mild testing with evolution and fairemail.
2025-04-11 10:06:34 +02:00
5a7d5fce98 run ineffassign (fast) before staticcheck (slow) 2025-04-07 18:40:54 +02:00
902de0e1f9 queue: in log lines about delivery, we had both "attempts" starting at 0 and "attempt" starting at 1, keep only "attempts" starting at 1
from eric l, thanks!
2025-04-07 13:35:42 +02:00
39c21f80cd imapserver: return proper response for FETCH of "BODY[1.MIME]" where 1 is a message
MIME returns the part headers. If 1 is a message, i.e. a message/rfc822 or
message/global, for example when top-level is a multipart/mixed, we were
returning the MIME headers from the message, not from the part.

We also shouldn't be returning a MIME-Version header or the separating newline
for MIME. Those are for MIME headers of a message, but the "MIME" fetch body
part is always about the part.

Found after looking into FETCH BODY handling for issue #327.
2025-04-07 12:15:13 +02:00
462568d878 webmail: for "cid"/content-id's used in html, look for them in all other parts, not just when there is a multipart/related in the message
The gmail apps generate messages consisting of multipart/mixed, with text/html
referring to a sibling image/jpeg. We weren't resolving that cid before.

Related to issue #327.
2025-04-07 11:10:14 +02:00
2defbce0bc imapserver: return all the extensible fields for bodystructure, notably for content-disposition
The gmail iOS/Android app were showing mime image parts as (garbled) text
instead of rendering them as image. By returning all the optional fields in the
bodystructure fetch attribute, the gmail app renders the image as expected by
the user. So we now add all fields. We didn't before, because we weren't
keeping track of Content-MD5, Content-Language and Content-Location header
fields, since they aren't that useful.

Messages in mailboxes have to be reparsed:
	./mox reparse

Without reparsing, imap responses will claim the extra fields
(content-disposition) are absent for existing messages, instead of not claiming
anything at all, which is what we did before.

Accounts and all/some mailboxes can get their "uid validity" bumped ("./mox
bumpuidvalidity $account [$mailbox]"), which should trigger clients to load all
messages from scratch, but gmail doesn't appear to notice, so it would be
better to remove & add the account in gmail.

For issue #327, also relevant to issue #217.
2025-04-05 15:46:17 +02:00
69d2699961 write base64 message parts with 76 data bytes on a line instead of 78
As required by RFC 2045 (MIME). The 78 byte lines work in practice, except that
SpamAssassin has rules that give messages with 78-byte lines spam points.

Mentioned by kjetilho on irc.
2025-04-03 10:22:15 +02:00
00c8db98e6 start more function names/calls with x when they handle errors through panics
mostly the imapserver and smtpserver connection write and read methods.
2025-04-02 13:59:46 +02:00
deb57462a4 update list of sponsors, add logo's and link to the nlnet projects 2025-04-02 11:24:59 +02:00
479bf29124 imapserver: implement the MULTISEARCH extension, with its ESEARCH command 2025-03-31 18:34:23 +02:00
5dcf674761 webmail: reconnect automatically in more cases
Before, we would only reconnect the SSE connection when the previous one lasted
10 minutes.  For some reason, firefox disconnects SSE connections when there is
any network change. Running the docker integration tests changes the network a
few time in quick succession, prevent further automatic reconnects.

This changes the "stop reconnection automatically" period from 10 minutes to 5
seconds.
2025-03-30 14:54:29 +02:00
aba0061073 small tweak to docs and website, mentioning EIA in the context of internalized email 2025-03-30 11:03:06 +02:00
cc5e3165ea imapserver: implement "inprogress" response code (RFC 9585) for keepalive during long search
For long searches in big mailboxes, without any matches, we would previously
keep working and not say anything. Clients could interpret this silence as a
broken connection at some point. We now send a "we're still searching" untagged
OK responses with code INPROGRESS every 10 seconds while we're still searching,
to prevent the client from closing the connection. We also send how many
messages we've processed, and usually also how many we need to process in grand
total. Clients can use this to show a progress bar.
2025-03-30 10:43:02 +02:00
3e128d744e for the web interfaces, ensure the effective configured http paths end in a slash to prevent 404's and/or errors accessing the web interfaces
The default paths for the web interfaces, such as /admin/, /account/, /webmail/
and /webapi/ end with a slash. They should end with a slash because we use the
path when restricting cookies to just that web interface. You could configure
paths not ending with a slash, but due to using http.StripPrefix, and our
handler, some of those requests may not work properly.

We now warn if configured paths don't end with a trailing slash when parsing
the config file. We normally error out when such things happen, but users
probably have paths without trailing slashes configured, and we don't want to
break them on a future upgrade. We now use an effective path that includes the
trailing slash.

We would always redirect requests to the configured paths but without trailing
slash to the path with trailing slash, and that stays.

For issue #325 by odama626.
2025-03-29 22:00:55 +01:00
3a3a11560e web interfaces: don't include version number in html, only return it after authentication
second round for issue #322
2025-03-29 20:46:53 +01:00
eeeabdc6de fix build with previous commit that didn't sync frontend
not at my sharpest...
2025-03-29 20:16:05 +01:00
3ac38aacca imapserver: fix storing previews when requested over imap and they are missing from the database
found while testing.
2025-03-29 20:13:10 +01:00
6ab31c15b7 imapserver: actually announce PREVIEW extension 2025-03-29 18:28:33 +01:00
a5d74eb718 webmail: add buttons to download a message as eml, and export 1 or more messages as mbox/maildir in zip/tgz/tar, like for entire mailboxes
Download as eml is useful with firefox, because opening the raw message in a
new tab, and then downloading it, causes firefox to request the url without
cookies, causing it to save a "403 - forbidden" response.

Exporting a selection is useful during all kinds of testing. Makes it easy to
an entire thread, or just some messages.

The export popover now has buttons for each combination of mbox/maildir vs
zip/tgz/tar. Before you may have had to select the email format and archive
format first, followed by a click. Now it's just a click.
2025-03-29 18:10:23 +01:00
d6e55b5f36 don't use strings.Lines, it's only available in go1.24 and we support go1.23 too 2025-03-28 18:20:18 +01:00
68729fa5a3 in smtp banner and imap ID command response when unauthenticated, don't send the mox version number
Attackers scanning the internet can use it to easily create a database of
hosts, software and versions. Let's not make it too easy to find old versions
that may be vulnerable to potential bugs found in the future. We could try
hiding the name "mox" as well, but the banner will still be identifyable, so
there isn't much point, and the public knowing approximately which software is
running can be useful for debugging.

The ID command in IMAP is used by clients to announce their software and
version. We only respond with our version when the user is authenticated.

There are still ways to discover the version number. But they don't involve
standard banner scanning, so someone would have to specifically target mox. We
could tighten that in the future.

For issue #322, based on email. Thanks everyone for discussing.
2025-03-28 17:50:40 +01:00
789e4875ca update to latest bstore 2025-03-28 17:39:20 +01:00
6bf80d91bc sync frontend api doc/client
Forgot to build after change just before commit...
2025-03-28 17:39:20 +01:00
aa631c604c imapserver: implement PREVIEW extension (RFC 8970), and store previews in message database
We were already generating previews of plain text parts for the webmail
interface, but we didn't store them, so were generating the previews each time
messages were listed.

Now we store previews in the database for faster handling. And we also generate
previews for html parts if needed. We use the first part that has textual
content.

For IMAP, the previews can be requested by an IMAP client. When we get the
"LAZY" variant, which doesn't require us to generate a preview, we generate it
anyway, because it should be fast enough. So don't make clients first ask for
"PREVIEW (LAZY)" and then again a request for "PREVIEW".

We now also generate a preview when a message is added to the account. Except
for imports. It would slow us down, the previews aren't urgent, and they will
be generated on-demand at first-request.
2025-03-28 17:10:17 +01:00
8b418a9ca2 update golang.org/x dependencies 2025-03-28 17:01:12 +01:00
027e5754a0 update to go1.23 and replace golang.org/x/exp/maps with stdlib maps 2025-03-28 17:01:06 +01:00
7a87522be0 rename variables, struct fields and functions to include an "x" when they can panic for handling errors
and document the convention in develop.txt.
spurred by running errcheck again (it has been a while). it still has too many
false to enable by default.
2025-03-24 16:12:22 +01:00
a2c79e25c1 check and log errors more often in deferred cleanup calls, and log remote-induced errors at lower priority
We normally check errors for all operations. But for some cleanup calls, eg
"defer file.Close()", we didn't. Now we also check and log most of those.
Partially because those errors can point to some mishandling or unexpected code
paths (eg file unexpected already closed). And in part to make it easier to use
"errcheck" to find the real missing error checks, there is too much noise now.

The log.Check function can now be used unconditionally for checking and logging
about errors. It adjusts the log level if the error is caused by a network
connection being closed, or a context is canceled or its deadline reached, or a
socket deadline is reached.
2025-03-24 14:06:05 +01:00
15a8ce8c0b fix warnings by ineffassign, with a one actual issue
In store/search.go, we would make a copy of a byte array, but then still use
the original instead of the copy. Could result in search operations not finding
messages that do have the content, but under very unlikely conditions only.

We'll keep running ineffassign with "make check", useful enough.
2025-03-24 10:25:33 +01:00
04b1f030b7 update to latest bstore, which now properly handles modifications during Query.ForEach 2025-03-24 10:02:50 +01:00
88ec5c6fbe add rfc 4155 about mbox files, and cross-reference in the import/export code for mbox files 2025-03-23 13:59:09 +01:00
a68a9d8a48 check whether mailboxes have message/etc counts through an "upgrade" boolean flag
Instead of using the per-mailbox flag, and going through all mailboxes when
opening an account.
2025-03-23 12:52:59 +01:00
b37faa06bd After queueing a message in the web api's, prevent context cancelation from completing message changes
Adding to the queue is done in a transaction, the queue db file is mox-global.
Appending the message to the Sent folder, removing it from Drafts, marking the
original message as answered/forwarded, is done in a separate database
transaction that gets the ctx passed in. If the ctx was canceled in between,
the queueing was finished, but the rest wasn't completed.

Reported by mteege, thanks!
2025-03-23 11:07:39 +01:00
b0e4dcdb61 sync to latest autocert 2025-03-21 21:47:59 +01:00
773d8cc959 update to latest github.com/mjl-/adns, synced to go1.24.1 2025-03-21 18:42:02 +01:00
70aedddc90 webmail: when composing, no longer remove the last remaining To address with the ctrl+backspace shortcut
On reply, with too many Cc/Bcc, I usually hit ctrl+backspace a few time. I just
want to clear the addresses, but I practically always still want a To address.
2025-03-21 13:51:53 +01:00
297e83188c Check for queued messages when removing an address, and more completely cleanup accounts when removing.
When removing an address, we want to make sure any queued messages for the
account still still have their address associated with the account. E.g.
through a catchall address.

Before removing an account, we fail deliveries still in the queue for the
account. We remove any addresses on the suppression list (which are stored in
the queue database, not the account database file that is removed completely).
We also clear all sessions for the webmail/webaccount interfaces. For the
webmail, further operations will fail, and the reconnection attempt will cause
the login popup with a message about an unknown session token.
2025-03-21 13:36:10 +01:00
75036c3a71 Before moving message files in imapserver and webmail API, ensure the message destination directory for the newly assigned IDs exist.
Example symptom, when deleting a message in the webmail (which moves to Trash):

        l=error m="duplicating message in old mailbox for current sessions" err="link data/accounts/mjl/msg/I/368638 data/accounts/mjl/msg/J/368640: no such file or directory" pkg=webmail

Problem introduced a few weeks ago, where moving messages starting duplicating
the message first, and the copy is erased once all references (in IMAP
sessions) to the old mailbox have been removed.
2025-03-21 10:18:39 +01:00
99f9eb438f Minor cleanup: use the ModSeq from the Mailbox in a ChangeMailboxAdd, no need to add the ModSeq again 2025-03-20 00:10:47 +01:00
9ca50ab207 imapserver: When trying to replace a message in a non-existent mailbox, do still consume the message if it is a non-synchronized literal
Not likely to happen in the wild.
2025-03-19 22:00:34 +01:00
5294a63c26 When logging structs, do log fields of type time.Time (timestamps)
The simplistic logging approach we've followed so far is to not log struct
fields that are themselves structs, which time.Time is. So we skipped, but do
log it now.
2025-03-19 21:52:31 +01:00
719dc2bee1 webmail: Don't abort SSE connection when a metadata/annotation change is made (broadcasted)
Missing case...
2025-03-16 14:02:45 +01:00