81 lines
2.2 KiB
Go
81 lines
2.2 KiB
Go
// Package imap provides an IMAP4rev2 server backed by the encrypted SQLite store.
|
|
package imap
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/emersion/go-imap/v2"
|
|
"github.com/emersion/go-imap/v2/imapserver"
|
|
|
|
"ghb.freebede.com/nahakubuilder/mailgosend/internal/auth"
|
|
"ghb.freebede.com/nahakubuilder/mailgosend/internal/config"
|
|
appCrypto "ghb.freebede.com/nahakubuilder/mailgosend/internal/crypto"
|
|
"ghb.freebede.com/nahakubuilder/mailgosend/internal/db"
|
|
)
|
|
|
|
// Deps groups dependencies needed by the IMAP server.
|
|
type Deps struct {
|
|
DB *db.DB
|
|
Crypt *appCrypto.Crypto
|
|
Brute *auth.BruteGuard
|
|
Cfg *config.Config
|
|
}
|
|
|
|
// NewServer creates an IMAP4rev2 server for port 143 (STARTTLS).
|
|
func NewServer(d *Deps, tlsCfg *tls.Config) *imapserver.Server {
|
|
return imapserver.New(&imapserver.Options{
|
|
NewSession: func(c *imapserver.Conn) (imapserver.Session, *imapserver.GreetingData, error) {
|
|
clientIP := connRemoteIP(c)
|
|
if d.Brute != nil && d.Cfg.BruteMaxTries > 0 {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
if banned, err := d.Brute.IsBanned(ctx, clientIP); err == nil && banned {
|
|
cancel()
|
|
return nil, nil, fmt.Errorf("connection refused")
|
|
}
|
|
cancel()
|
|
}
|
|
return &IMAPSession{deps: d, clientIP: clientIP}, &imapserver.GreetingData{}, nil
|
|
},
|
|
Caps: imap.CapSet{
|
|
imap.CapIMAP4rev2: {},
|
|
imap.CapIMAP4rev1: {},
|
|
},
|
|
TLSConfig: tlsCfg,
|
|
InsecureAuth: tlsCfg == nil,
|
|
})
|
|
}
|
|
|
|
// ListenAndServe listens on addr (port 143 / STARTTLS).
|
|
func ListenAndServe(s *imapserver.Server, addr, name string) error {
|
|
ln, err := net.Listen("tcp", addr)
|
|
if err != nil {
|
|
return fmt.Errorf("%s listen %s: %w", name, addr, err)
|
|
}
|
|
log.Printf("[%s] listening on %s", name, addr)
|
|
return s.Serve(ln)
|
|
}
|
|
|
|
// ListenAndServeTLS listens with implicit TLS (port 993 / IMAPS).
|
|
func ListenAndServeTLS(s *imapserver.Server, addr, name string) error {
|
|
log.Printf("[%s] listening on %s (TLS)", name, addr)
|
|
return s.ListenAndServeTLS(addr)
|
|
}
|
|
|
|
// connRemoteIP returns the client IP from an imapserver.Conn.
|
|
func connRemoteIP(c *imapserver.Conn) string {
|
|
if c == nil {
|
|
return ""
|
|
}
|
|
nc := c.NetConn()
|
|
if nc == nil {
|
|
return ""
|
|
}
|
|
host, _, _ := net.SplitHostPort(nc.RemoteAddr().String())
|
|
return host
|
|
}
|