Initial commit

This commit is contained in:
2018-03-05 12:19:04 +00:00
commit 811b90224f
114 changed files with 16465 additions and 0 deletions

62
internal/imap/server.go Normal file
View File

@@ -0,0 +1,62 @@
package imap
import (
"context"
"io"
"log"
imapbackend "github.com/emersion/go-imap/backend"
imapserver "github.com/emersion/go-imap/server"
"ur.gs/crockery/internal/store"
)
type Server interface {
Run()
io.Closer
}
func NewServer(cancel context.CancelFunc, datastore store.Interface, starttls bool) Server {
out := &concrete{
cancel: cancel,
store: datastore,
}
out.server = imapserver.New(out)
if starttls {
out.server.Addr = ":143"
} else {
out.server.Addr = ":993"
}
return out
}
type concrete struct {
cancel context.CancelFunc
store store.Interface
server *imapserver.Server
}
func (c *concrete) Run() {
if err := c.server.ListenAndServe(); err != nil {
log.Printf("Error serving IMAP %s: %v", c.server.Addr, err)
} else {
log.Printf("Stopped listening on IMAP %s", c.server.Addr)
}
c.cancel()
}
// backend implementation for go-smtp
func (c *concrete) Login(string, string) (imapbackend.User, error) {
return nil, nil
}
func (c *concrete) Close() error {
c.cancel() // FIXME: this doesn't touch the server
return nil
}

View File

@@ -0,0 +1,69 @@
package services
import (
"context"
"io"
"log"
"ur.gs/crockery/internal/imap"
"ur.gs/crockery/internal/smtp"
"ur.gs/crockery/internal/store"
)
type Interface interface {
Run() // Run the services
}
func New(ctx context.Context, datastore store.Interface) (Interface, error) {
ctx, cancelFunc := context.WithCancel(ctx)
return &concrete{
ctx: ctx,
cancel: cancelFunc,
store: datastore,
}, nil
}
type genServer interface {
Run()
io.Closer
}
type concrete struct {
ctx context.Context
cancel context.CancelFunc
store store.Interface
// This is only mutated in the Run() method
servers []genServer
}
// For now, bind unconditionally to these ports, on IPv4 + IPv6:
// 25 - incoming SMTP (STARTTLS)
// 587 - outgoing SMTP (STARTTLS)
// 143 - IMAP (STARTTLS)
// 993 - IMAP (TLS)
//
// Each listener gets incoming connections serviced against the passed-in store.
func (c *concrete) Run() {
c.servers = []genServer{
smtp.NewServer(c.cancel, c.store, false),
smtp.NewServer(c.cancel, c.store, true),
imap.NewServer(c.cancel, c.store, false),
imap.NewServer(c.cancel, c.store, true),
}
log.Print("Running all services")
for _, srv := range c.servers {
go srv.Run()
}
<-c.ctx.Done()
log.Print("Closing all services")
for _, srv := range c.servers {
srv.Close()
}
}

62
internal/smtp/server.go Normal file
View File

@@ -0,0 +1,62 @@
package smtp
import (
"context"
"io"
"log"
"github.com/emersion/go-smtp"
"ur.gs/crockery/internal/store"
)
type Server interface {
Run()
io.Closer
}
func NewServer(cancel context.CancelFunc, datastore store.Interface, submission bool) Server {
out := &concrete{
cancel: cancel,
store: datastore,
}
out.server = smtp.NewServer(out)
out.server.Domain = datastore.Domain()
if submission {
out.server.Addr = ":587"
} else {
out.server.Addr = ":25"
}
return out
}
type concrete struct {
cancel context.CancelFunc
store store.Interface
server *smtp.Server
}
func (c *concrete) Run() {
if err := c.server.ListenAndServe(); err != nil {
log.Printf("Error serving SMTP %s: %v", c.server.Addr, err)
} else {
log.Printf("Stopped listening on SMTP %s", c.server.Addr)
}
c.cancel()
}
// backend implementation for go-smtp
func (c *concrete) Login(string, string) (smtp.User, error) {
return nil, nil
}
func (c *concrete) Close() error {
c.cancel() // FIXME: this doesn't touch the server
return nil
}

21
internal/store/store.go Normal file
View File

@@ -0,0 +1,21 @@
package store
import (
"context"
)
type Interface interface {
Domain() string
}
func New(ctx context.Context, filename string) (Interface, error) {
return &concrete{domain: "example.com"}, nil
}
type concrete struct {
domain string
}
func (c *concrete) Domain() string {
return c.domain
}