Skip to content

Commit

Permalink
Added initial support for the STARTTLS command.
Browse files Browse the repository at this point in the history
It starts the TLS connection using the already existing connection, and
replaces all readers/writers in use to make sure they're reading from
the correct location (meaning they won't notice a difference).

Associated with alienscience#23.
  • Loading branch information
EtienneBruines committed Jun 25, 2015
1 parent dc497ef commit 01bf705
Show file tree
Hide file tree
Showing 12 changed files with 1,242 additions and 67 deletions.
33 changes: 27 additions & 6 deletions command.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package imapsrv

import (
"crypto/tls"
"fmt"
"net/textproto"
"strings"
)

Expand Down Expand Up @@ -39,7 +41,26 @@ func (c *capability) execute(s *session) *response {
// The IMAP server is assumed to be running over SSL and so
// STARTTLS is not supported and LOGIN is not disabled
return ok(c.tag, "CAPABILITY completed").
extra("CAPABILITY IMAP4rev1")
extra("CAPABILITY IMAP4rev1 STARTTLS")
}

//------------------------------------------------------------------------------

type starttls struct {
tag string
parser *parser
}

func (c *starttls) execute(sess *session) *response {
sess.conn.Write([]byte(fmt.Sprintf("%s Begin TLS negotiation now", c.tag)))

sess.conn = tls.Server(sess.conn, &tls.Config{Certificates: sess.listener.certificates})
textConn := textproto.NewConn(sess.conn)

// replace current lexer reader
c.parser.lexer.reader = &textConn.Reader

return empty().replaceBuffers(textConn.R, textConn.W)
}

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -155,7 +176,7 @@ func (c *list) execute(sess *session) *response {
// Convert the reference and mbox pattern into slices
ref := pathToSlice(c.reference)
mbox := pathToSlice(c.mboxPattern)

// Get the list of mailboxes
mboxes, err := sess.list(ref, mbox)

Expand All @@ -172,8 +193,8 @@ func (c *list) execute(sess *session) *response {
res := ok(c.tag, "LIST completed")
for _, mbox := range mboxes {
res.extra(fmt.Sprintf(`LIST (%s) "%s" /%s`,
joinMailboxFlags(mbox),
string(pathDelimiter),
joinMailboxFlags(mbox),
string(pathDelimiter),
strings.Join(mbox.Path, string(pathDelimiter))))
}

Expand Down Expand Up @@ -224,7 +245,7 @@ func pathToSlice(path string) []string {
// Remove leading and trailing blanks
if ret[0] == "" {
if len(ret) > 1 {
ret = ret[1:len(ret)]
ret = ret[1:]
} else {
return []string{}
}
Expand All @@ -240,7 +261,7 @@ func pathToSlice(path string) []string {
}

return ret

}

// Return a string of mailbox flags for the given mailbox
Expand Down
28 changes: 9 additions & 19 deletions command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,16 @@ package imapsrv
import "testing"
import "fmt"



func setupTest() (*Server,*session){
func setupTest() (*Server, *session) {
m := &TestMailstore{}
s := NewServer(
Store(m),
)
//s.Start()
sess := createSession(1, s.config)
sess := createSession(1, s.config, s, nil, nil) // TODO: listener and net.Conn
return s, sess
}



// A test mailstore used for unit testing
type TestMailstore struct {
}
Expand All @@ -34,20 +30,20 @@ func (m *TestMailstore) GetMailboxes(path []string) ([]*Mailbox, error) {
if len(path) == 0 {
// Root
return []*Mailbox{
&Mailbox{
{
Name: "inbox",
Path: []string{"inbox"},
Id: 1,
},
&Mailbox{
{
Name: "spam",
Path: []string{"spam"},
Id: 2,
},
}, nil
} else if len(path) == 1 && path[0] == "inbox" {
return []*Mailbox{
&Mailbox{
{
Name: "starred",
Path: []string{"inbox", "stared"},
Id: 3,
Expand All @@ -58,7 +54,6 @@ func (m *TestMailstore) GetMailboxes(path []string) ([]*Mailbox, error) {
}
}


// Get the sequence number of the first unseen message
func (m *TestMailstore) FirstUnseen(mbox int64) (int64, error) {
return 4, nil
Expand All @@ -79,26 +74,21 @@ func (m *TestMailstore) NextUid(mbox int64) (int64, error) {
return 9, nil
}




func TestCapabilityCommand( t *testing.T){
func TestCapabilityCommand(t *testing.T) {
_, session := setupTest()
cap := &capability{tag: "A00001"}
resp := cap.execute(session)
if (resp.tag != "A00001") || (resp.message != "CAPABILITY completed") || (resp.untagged[0] != "CAPABILITY IMAP4rev1"){
if (resp.tag != "A00001") || (resp.message != "CAPABILITY completed") || (resp.untagged[0] != "CAPABILITY IMAP4rev1") {
t.Error("Capability Failed - unexpected response.")
fmt.Println(resp)
}
}



func TestLogoutCommand( t *testing.T){
func TestLogoutCommand(t *testing.T) {
_, session := setupTest()
log := &logout{tag: "A00004"}
resp := log.execute(session)
if (resp.tag != "A00004") || (resp.message != "LOGOUT completed") || (resp.untagged[0] != "BYE IMAP4rev1 Server logging out"){
if (resp.tag != "A00004") || (resp.message != "LOGOUT completed") || (resp.untagged[0] != "BYE IMAP4rev1 Server logging out") {
t.Error("Logout Failed - unexpected response.")
fmt.Println(resp)
}
Expand Down
52 changes: 52 additions & 0 deletions demo/certs/private.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
SE5gPKJ+QTJu5ptpzP+jdwbbL2jycobF
-----END PRIVATE KEY-----
30 changes: 30 additions & 0 deletions demo/certs/public.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFOzCCAyOgAwIBAgIJAMsxSTftv1fZMA0GCSqGSIb3DQEBCwUAMDQxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMRAwDgYDVQQKDAdpbWFwc3J2MB4X
DTE1MDYyNTE2NDU1M1oXDTE4MDQyNTE2NDU1M1owNDELMAkGA1UEBhMCQVUxEzAR
BgNVBAgMClNvbWUtU3RhdGUxEDAOBgNVBAoMB2ltYXBzcnYwggIiMA0GCSqGSIb3
DQEBAQUAA4ICDwAwggIKAoICAQC3BkUgs7GyT9u5h4NOUpo9ip8OvHXeVJ/OCsmV
2Se6mfAl1chuyaye6SizfmWlN9c8cKbHuZ766y0EyeXNy8xApmHkq7Ew2UQCR0Dj
YkOdcCh/1069AAjjPkVto4m9aedPrMFSKV0YpAa57VxoetUFdNFlTe6T+N2mQkz1
G8WdCdNtiXj4HZ8w+LfdoZ9VDqiq6TVka3sqUCIgoPtKXk1Ze7hpNtQHxr/o3C9r
n3txAFGmcg2PLbP7tkIwnJ6xw4Aaa8rOepJtDPW4oER9BE74DLRoue60Gv5UVXyC
0Rmq9qRacK0ouU3Cu7w4TQmXTkNiuqalfe1gURyptTZOfpZKk9+3oMLWuh8jAWrM
3EI6N70ncSNllIwk4safV/mLNWIuj0UzubN7hNv1rVXPP8hKp0QT1z/SEezJeyRc
n5ksb+NojpZgxqhikPGx+iiqg7l2mbnvi4Uj8lEXom+qO/WRDjDXjSd8kXOQnA2P
V1p3ScQ/gxNcg8TLWbJpyzFUN8Fdn8uV7kjcZRLl9Pf7rsiSixx7XAbyMe2lX627
AfpP2eu3xrxJ40d3/17ExoR1E5zQkM+a1ZUMMTwSeKfuDHeW8wtbCzFXFVxg0AJO
E+XS3U9+TsgKpyBdB/Jc8795+nluRsehPUmTnWu1INHH5W4QMmMITYluj/kxEoP+
XdByUQIDAQABo1AwTjAdBgNVHQ4EFgQUXD5qtFRS4SRtT1Z5eiwzOfElXbYwHwYD
VR0jBBgwFoAUXD5qtFRS4SRtT1Z5eiwzOfElXbYwDAYDVR0TBAUwAwEB/zANBgkq
hkiG9w0BAQsFAAOCAgEAFAAKmS6QZdhaDAl5Wsq7dUQRqFqvhrfM0Zt0Rl+xSem+
LVDJ21BwcUJIUemOM4I2cYIL/PtGI2DBrYEafloPRMdAB4y22mFezJCl41Kijfc7
vGGy3zGYTByp2vu4MuLNgvate8vDeAlNfXVhQxt1erxgrxfT2KQS1zryRr5On0+j
2ES/6El6bFWo/hxxeiDZyPdPXyW0vXBXofe3MBS/DZb2xBiVv8y3V5+NvMxg082C
s6wbKSAsn5v6sgM4Qi6IF4JZ02nFoD1tMLHNmqqzpff52m2YLAcuDUtswfDP6QTP
Z2KWMVmvt5ovCA0RXrsgWGyYGBtiLgCbiUJ1BajVLCcowYBGT5smOnMAXxHEt2DD
R5v4K6WG7HHkn2Afozxssz41rCUjnkAgkURk9rmY8kqULSM7X3n5PPvDcw6wfNrM
jG6M0oiUelNpkxTiNLyFwHeWG9VLUmQN8zMx9ldvv04apGo9/+JsrXa9x8Kl0Og4
D+CKVDaXjwyqDI7pRVkVdUdu4rXTc4SDYvIyUOOugy4Uzw9IbgQFcQCuF2HMcLUy
tUa+1mLcNlWMaFUrO4h2KgoJHRKS2UmgyQ73xxybsxz+hvnqzWVccCvPjZh0cU7o
1pSVmBhle5L/N5y4ZisEOR4iuZgkwK5w029u6MvGOu+CXZYDghTEcuyZUsM4+fQ=
-----END CERTIFICATE-----
3 changes: 1 addition & 2 deletions demo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func main() {

s := imap.NewServer(
imap.Listen("127.0.0.1:1193"),
imap.Listen("127.0.0.1:1194"),
imap.ListenSTARTTLS("127.0.0.1:1194", "certs/public.pem", "certs/private.pem"),
imap.Store(m),
)

Expand All @@ -25,4 +25,3 @@ func main() {
log.Print("IMAP server not started")
}
}

Loading

0 comments on commit 01bf705

Please sign in to comment.