jupemail/server.go

116 lines
2.6 KiB
Go

package main
import (
"bytes"
"github.com/chrj/smtpd"
"github.com/francoispqt/gojay"
"io/ioutil"
"jupemail/db"
)
type Mail struct {
Sender string `multipart:"sender"`
Recipient string `multipart:"recipient"`
Message *Message `multipart:"message"`
}
func NewMail(sender, recipient string, msg *Message) *Mail {
return &Mail{Sender: sender, Recipient: recipient, Message: msg}
}
type Server struct {
srv *smtpd.Server
Addresses map[string]Broker
CatchAll bool
}
func NewServer(newsmtpd *smtpd.Server) *Server {
a := make(map[string]Broker)
a["kayos@crip.haus"] = NewIRCBroker("#go")
mx := &Server{
srv: newsmtpd,
Addresses: a,
}
mx.CatchAll = false
mx.srv.Handler = mx.ServeSMTP
mx.srv.RecipientChecker = mx.CheckRecipient
mx.srv.ConnectionChecker = mx.CheckConn
return mx
}
func (s *Server) CheckConn(p smtpd.Peer) error {
ident := &SMTPSource{Actual: p}
if Rater.Check(ident) {
// we probably shouldn't output logs here or
// we're gonna DoS ourselves, maybe start doing batches
return smtpd.Error{
Code: 421,
Message: "you are being ratelimited",
}
}
return nil
}
func (s *Server) CheckRecipient(p smtpd.Peer, addr string) error {
if s.CatchAll {
return nil
}
if _, ok := s.Addresses[addr]; ok {
return nil
}
log.Warn().Str("caller", p.Addr.String()).Str("address", addr).
Msg("denied unknown recipient address")
return smtpd.Error{
Code: 550,
Message: "address rejected: user unknown in local recipient table",
}
}
func (s *Server) ServeSMTP(p smtpd.Peer, env smtpd.Envelope) error {
msg, err := NewMessage(bytes.NewBuffer(env.Data))
if err != nil {
log.Warn().Str("caller", p.Addr.String()).Strs("address", env.Recipients).
Msg("failed to parse mime, rejecting")
return smtpd.Error{
Code: 554,
Message: "mail rejected: invalid format",
}
}
for _, addr := range env.Recipients {
if _, ok := s.Addresses[addr]; !ok {
return smtpd.Error{
Code: 451,
Message: "internal server error",
}
}
buf := bytes.NewBuffer(nil)
enc := gojay.NewEncoder(buf)
enc.Encode(NewMail(env.Sender, addr, msg))
if err := enc.Encode(NewMail(env.Sender, addr, msg)); err != nil {
log.Error().Err(err).Msg("could not encode request body")
return smtpd.Error{
Code: 451,
Message: "internal server error",
}
}
d, err := ioutil.ReadAll(buf)
if err != nil {
log.Error().Interface("envelope", env).Err(err).Msg("!!!")
}
go s.deliverMessage(addr, d)
}
return nil
}
func (s *Server) deliverMessage(addr string, d []byte) {
err = db.CompressAndStore(addr, d)
if err != nil {
log.Error().Err(err).Msg("!!!")
}
s.Addresses[addr].Deliver(d)
}