jordwest / imap-server Goto Github PK
View Code? Open in Web Editor NEWIMAP Server for golang
License: MIT License
IMAP Server for golang
License: MIT License
Are you planning on adding other features like the mail store? or have you stopped working on the project?
A message is no longer considered 'Recent' after a mailbox has been selected the message has been fetched
Expunge should permanently delete all messages marked with the flag \Deleted
A CHECK
should be the same as a NOOP
, but also call a function on the Mailstore to prompt it to do any cleanup, refreshing, re-caching etc. as required
Finish implementing STORE for updating a message's flags
Request Example:
UID FETCH 20 (BODY.PEEK[] BODYSTRUCTURE)
From the RFC:
Arguments: sequence set, mailbox name
Responses: no specific responses for this command
Result:
OK - copy completed
NO - copy error: can't copy those messages or to that name
BAD - command unknown or arguments invalid
The COPY command copies the specified message(s) to the end of the specified destination mailbox. The flags and internal date of the message(s) SHOULD be preserved, and the Recent flag SHOULD be set,
in the copy.
If the destination mailbox does not exist, a server SHOULD return an error. It SHOULD NOT automatically create the mailbox. Unless it is certain that the destination mailbox can not be created, the server MUST send the response code "[TRYCREATE]" as the prefix of the text of the tagged NO response. This gives a hint to the client that it can attempt a CREATE command and retry the COPY if the CREATE is successful.
If the COPY command is unsuccessful for any reason, server implementations MUST restore the destination mailbox to its state before the COPY attempt.
Example: C: A003 COPY 2:4 MEETING
S: A003 OK COPY completed
Proposal
Flags, headers and body of the message except the UID and sequence numbers MUST be preserved. The UID MUST change to the latest unassigned value for the given mailbox and sequence number will also have the latest value. The server MUST NOT automatically create the target mailbox.
Check for any remaining hardcoded responses which should be using Mailstore interface
DummyMailstore currently returns fixed responses for some requests. It should be capable of storing emails in-memory. Currently some of this work is done, with a few sample emails stored in a slice. However functions like MessageSetBySequenceNumber
still return hardcoded responses. DummyMailstore should be a full demonstration implementation of a mail storage.
Test code is starting to get messy, look into switching to Ginkgo or similar
Restarting server and running tests manually during development is tedious, set up fswatch for re-running tests and restarting server
go test
STORE
command is not recognised when Apple Mail tries to store it's own user defined flags, partly because of a $
character in the flag name.
Should support storage of user-defined flags if the Mailstore implementor chooses to.
The current response to an unsupported command is:
C: 10 uid copy 10:11,13 "Trash"
S: * BAD Command not understood
It should be
C: 10 uid copy 10:11,13 "Trash"
S: 10 BAD Command not understood
commands.go
is getting unwieldy, should separate commands or at least command groups out into separate source files
Some mail clients do unusual things - eg Apple mail uses it's own flags which are currently not recognised by the server.
Create test specs of sample conversations for various mail clients based on observed conversations.
The RFC specifies that UIDs and sequence numbers are unsigned 32 bit integers
A NOOP
command from the client currently does not do anything. However a NOOP
should trigger the server to check the mailbox for new messages and notify the client using EXISTS
etc.
The Go Race detector complains about a data race between Listen() and Close(). To illustrate, apply the following patch to create an example test case:
diff --git a/server_test.go b/server_test.go
index a242138..c50dd16 100644
--- a/server_test.go
+++ b/server_test.go
@@ -6,6 +6,7 @@ import (
"os"
"regexp"
"testing"
+ "time"
"github.com/jordwest/imap-server/conn"
"github.com/jordwest/imap-server/mailstore"
@@ -288,3 +289,13 @@ func TestFetchFullMessageByUID(t *testing.T) {
r.expect(t, " UID 11)")
r.expect(t, "abcd.123 OK UID FETCH Completed")
}
+
+func TestDataRace(t *testing.T) {
+ s := NewServer(mailstore.NewDummyMailstore())
+ s.Addr = "127.0.0.1:10143"
+ go func() {
+ s.ListenAndServe()
+ }()
+ time.Sleep(time.Millisecond)
+ s.Close()
+}
Then run go test -race -run=DataRace
. The race detector should dump something like the following: (edited for readability)
==================
WARNING: DATA RACE
Read by goroutine 5:
github.com/jordwest/imap-server.(*Server).Close()
…/src/github.com/jordwest/imap-server/server.go:83 +0xcf
github.com/jordwest/imap-server.TestDataRace()
…/src/github.com/jordwest/imap-server/server_test.go:300 +0x2cd
…
Previous write by goroutine 6:
github.com/jordwest/imap-server.(*Server).Listen()
…/src/github.com/jordwest/imap-server/server.go:55 +0x481
github.com/jordwest/imap-server.(*Server).ListenAndServe()
…/src/github.com/jordwest/imap-server/server.go:36 +0x47
…
==================
==================
WARNING: DATA RACE
Read by goroutine 5:
net.(*TCPListener).Close()
…/go-1.4.2/src/net/tcpsock_posix.go:254 +0x4f
github.com/jordwest/imap-server.(*Server).Close()
…/src/github.com/jordwest/imap-server/server.go:86 +0x229
…
Previous write by goroutine 6:
net.ListenTCP()
…/go-1.4.2/src/net/tcpsock_posix.go:298 +0x4aa
net.Listen()
…/go-1.4.2/src/net/dial.go:266 +0x559
github.com/jordwest/imap-server.(*Server).Listen()
…/src/github.com/jordwest/imap-server/server.go:50 +0x307
github.com/jordwest/imap-server.(*Server).ListenAndServe()
…/src/github.com/jordwest/imap-server/server.go:36 +0x47
…
==================
As this project is young and there are many ways to address this (RWMutex, signaling over chans, etc), I am not submitting a pull request.
This is also not a critical race, since it happens on shutdown, but it does clutter the output of go test -race
, which makes it harder to sort out application races.
Thanks again!
This would reduce the impact of PLAIN
-only authentication a bit.
Possible approach that I took in my toy server:
cert, err := tls.LoadX509KeyPair("/tmp/goimapd.crt", "/tmp/goimapd.key")
if err != nil {
fmt.Printf("loadkeys: %s\n", err)
textconn.PrintfLine("%s BAD Sorry, server-side problem loading certs.", tag, cmd)
return
}
tlsconfig := &tls.Config{
Certificates: []tls.Certificate{cert},
}
textconn.PrintfLine("%s OK STARTTLS commencing.", tag)
conn = tls.Server(conn, tlsconfig)
textconn = textproto.NewConn(conn)
It looks like a similar approach could be implemented in imap-server, too: the STARTTLS
command could replace c.Rwc
with an instance of tls.Server
.
Create a MessageRangeSpecifier
type which handles numeric identification of messages and distinguishes between single messages (UID 5, or 2), ranges of messages (UID 3:10, 15:17), or unbounded ranges (UID 6:*)
Create helper function that interprets the message UID/sequence number range and calls the relevant function on the Mailbox interface, returning the list of Messages.
all commands_*.go files should go in a sub folder/package
See RFC 3501 - 6.3.11
When mutt attempts to fetch the body of a message, the client sends:
C: a0006 UID FETCH 10 BODY.PEEK[]
Which doesn't match the pattern in conn/commands.go:
registerCommand("((?i)UID )?(?i:FETCH) ("+sequenceSet+") \\(([A-z0-9\\s\\(\\)\\[\\]\\.-]+)\\)", cmdFetch)
Do you have a client that has explicit parens around the 'BODY.PEEK[]' part? I'm not seeing anything to that effect in http://tools.ietf.org/html/rfc3501#section-6.4.5
PS, you may want to check out string literals specified with backticks instead of double-quotes when writing regexps. It saves on some of the backslash escaping.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.