maddy/sqlmail.go
fox.cpp ee553a4cc4 Rework logging
Implement debug log (can be enabled using `debug` config directive)
Remove errors directive for IMAP endpoint module.
2019-03-30 17:34:19 +02:00

150 lines
3.3 KiB
Go

package maddy
import (
"bytes"
"errors"
"io"
"strings"
"time"
"github.com/emersion/go-imap/backend"
"github.com/emersion/maddy/config"
"github.com/emersion/maddy/log"
"github.com/emersion/maddy/module"
"github.com/foxcpp/go-sqlmail"
imapsqlmail "github.com/foxcpp/go-sqlmail/imap"
)
type SQLMail struct {
*imapsqlmail.Backend
instName string
Log log.Logger
}
func (sqlm *SQLMail) Name() string {
return "sqlmail"
}
func (sqlm *SQLMail) InstanceName() string {
return sqlm.instName
}
func (sqlm *SQLMail) Version() string {
return sqlmail.VersionStr
}
func NewSQLMail(instName string, globalCfg map[string]config.Node, rawCfg config.Node) (module.Module, error) {
var driver string
var dsn string
appendlimitVal := int64(-1)
opts := imapsqlmail.Opts{}
mod := SQLMail{
instName: instName,
Log: log.Logger{Out: log.StderrLog, Name: "sqlmail"},
}
cfg := config.Map{}
cfg.String("driver", false, true, "", &driver)
cfg.String("dsn", false, true, "", &dsn)
cfg.Int64("appendlimit", false, false, 32*1024*1024, &appendlimitVal)
cfg.Bool("debug", true, &mod.Log.Debug)
if _, err := cfg.Process(globalCfg, &rawCfg); err != nil {
return nil, err
}
if appendlimitVal == -1 {
opts.MaxMsgBytes = nil
} else {
opts.MaxMsgBytes = new(uint32)
*opts.MaxMsgBytes = uint32(appendlimitVal)
}
sqlm, err := imapsqlmail.NewBackend(driver, dsn, opts)
mod.Backend = sqlm
if err != nil {
return nil, err
}
return &mod, nil
}
func (sqlm *SQLMail) Extensions() []string {
return []string{"APPENDLIMIT", "MOVE", "CHILDREN"}
}
func (sqlm *SQLMail) Deliver(ctx module.DeliveryContext, msg io.Reader) error {
var buf bytes.Buffer
if _, err := io.Copy(&buf, msg); err != nil {
return err
}
for _, rcpt := range ctx.To {
parts := strings.Split(rcpt, "@")
if len(parts) != 2 {
sqlm.Log.Println("malformed address:", rcpt)
return errors.New("Deliver: missing domain part")
}
if _, ok := ctx.Opts["local_only"]; ok {
hostname := ctx.Opts["hostname"]
if hostname == "" {
hostname = ctx.OurHostname
}
if parts[1] != hostname {
sqlm.Log.Debugf("local_only, skipping %s", rcpt)
continue
}
}
if _, ok := ctx.Opts["remote_only"]; ok {
hostname := ctx.Opts["hostname"]
if hostname == "" {
hostname = ctx.OurHostname
}
if parts[1] == hostname {
sqlm.Log.Debugf("remote_only, skipping %s", rcpt)
continue
}
}
u, err := sqlm.GetExistingUser(parts[0])
if err != nil {
sqlm.Log.Debugf("failed to get user for %s: %v", rcpt, err)
return err
}
// TODO: We need to handle Ctx["spam"] here.
tgtMbox := "INBOX"
mbox, err := u.GetMailbox(tgtMbox)
if err != nil {
if err == backend.ErrNoSuchMailbox {
// Create INBOX if it doesn't exists.
sqlm.Log.Debugln("creating inbox for", rcpt)
if err := u.CreateMailbox(tgtMbox); err != nil {
sqlm.Log.Debugln("inbox creation failed for", rcpt)
return err
}
mbox, err = u.GetMailbox(tgtMbox)
if err != nil {
return err
}
} else {
sqlm.Log.Debugf("failed to get inbox for %s: %v", rcpt, err)
return err
}
}
if err := mbox.CreateMessage([]string{}, time.Now(), &buf); err != nil {
sqlm.Log.Debugf("failed to save msg for %s: %v", rcpt, err)
return err
}
}
return nil
}
func init() {
module.Register("sqlmail", NewSQLMail)
}