116 lines
2.6 KiB
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)
|
|
}
|