Files
2026-05-21 20:27:58 +00:00

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
}