1
0
Fork 0
mirror of https://github.com/binwiederhier/ntfy.git synced 2025-06-24 21:38:32 +02:00

Working prefix

This commit is contained in:
Philipp Heckel 2021-12-27 22:06:40 +01:00
parent 7eaa92cb20
commit e7f8fc93e4
2 changed files with 124 additions and 80 deletions

View file

@ -5,17 +5,25 @@ import (
"errors"
"github.com/emersion/go-smtp"
"io"
"io/ioutil"
"log"
"net/mail"
"strings"
"sync"
)
var (
errInvalidDomain = errors.New("invalid domain")
errInvalidAddress = errors.New("invalid address")
errInvalidTopic = errors.New("invalid topic")
errTooManyRecipients = errors.New("too many recipients")
)
// smtpBackend implements SMTP server methods.
type smtpBackend struct {
config *Config
sub subscriber
config *Config
sub subscriber
success int64
failure int64
mu sync.Mutex
}
func newMailBackend(conf *Config, sub subscriber) *smtpBackend {
@ -26,19 +34,24 @@ func newMailBackend(conf *Config, sub subscriber) *smtpBackend {
}
func (b *smtpBackend) Login(state *smtp.ConnectionState, username, password string) (smtp.Session, error) {
return &smtpSession{config: b.config, sub: b.sub}, nil
return &smtpSession{backend: b}, nil
}
func (b *smtpBackend) AnonymousLogin(state *smtp.ConnectionState) (smtp.Session, error) {
return &smtpSession{config: b.config, sub: b.sub}, nil
return &smtpSession{backend: b}, nil
}
func (b *smtpBackend) Counts() (success int64, failure int64) {
b.mu.Lock()
defer b.mu.Unlock()
return b.success, b.failure
}
// smtpSession is returned after EHLO.
type smtpSession struct {
config *Config
sub subscriber
from, to string
mu sync.Mutex
backend *smtpBackend
topic string
mu sync.Mutex
}
func (s *smtpSession) AuthPlain(username, password string) error {
@ -46,63 +59,84 @@ func (s *smtpSession) AuthPlain(username, password string) error {
}
func (s *smtpSession) Mail(from string, opts smtp.MailOptions) error {
s.mu.Lock()
defer s.mu.Unlock()
s.from = from
return nil
}
func (s *smtpSession) Rcpt(to string) error {
s.mu.Lock()
defer s.mu.Unlock()
addressList, err := mail.ParseAddressList(to)
if err != nil {
return err
} else if len(addressList) != 1 {
return errors.New("only one recipient supported")
} else if !strings.HasSuffix(addressList[0].Address, "@"+s.config.SMTPServerDomain) {
return errors.New("invalid domain")
} else if s.config.SMTPServerAddrPrefix != "" && !strings.HasPrefix(addressList[0].Address, s.config.SMTPServerAddrPrefix) {
return errors.New("invalid address")
}
// FIXME check topic format
s.to = addressList[0].Address
return nil
return s.withFailCount(func() error {
conf := s.backend.config
addressList, err := mail.ParseAddressList(to)
if err != nil {
return err
} else if len(addressList) != 1 {
return errTooManyRecipients
}
to = addressList[0].Address
if !strings.HasSuffix(to, "@"+conf.SMTPServerDomain) {
return errInvalidDomain
}
to = strings.TrimSuffix(to, "@"+conf.SMTPServerDomain)
if conf.SMTPServerAddrPrefix != "" {
if !strings.HasPrefix(to, conf.SMTPServerAddrPrefix) {
return errInvalidAddress
}
to = strings.TrimPrefix(to, conf.SMTPServerAddrPrefix)
}
if !topicRegex.MatchString(to) {
return errInvalidTopic
}
s.mu.Lock()
s.topic = to
s.mu.Unlock()
return nil
})
}
func (s *smtpSession) Data(r io.Reader) error {
s.mu.Lock()
defer s.mu.Unlock()
b, err := ioutil.ReadAll(r)
if err != nil {
return err
}
log.Println("Data:", string(b))
msg, err := mail.ReadMessage(bytes.NewReader(b))
if err != nil {
return err
}
body, err := io.ReadAll(msg.Body)
if err != nil {
return err
}
topic := strings.TrimSuffix(s.to, "@"+s.config.SMTPServerDomain)
m := newDefaultMessage(topic, string(body))
subject := msg.Header.Get("Subject")
if subject != "" {
m.Title = subject
}
return s.sub(m)
return s.withFailCount(func() error {
b, err := io.ReadAll(r) // Protected by MaxMessageBytes
if err != nil {
return err
}
msg, err := mail.ReadMessage(bytes.NewReader(b))
if err != nil {
return err
}
body, err := io.ReadAll(io.LimitReader(msg.Body, int64(s.backend.config.MessageLimit)))
if err != nil {
return err
}
m := newDefaultMessage(s.topic, string(body))
subject := msg.Header.Get("Subject")
if subject != "" {
m.Title = subject
}
if err := s.backend.sub(m); err != nil {
return err
}
s.backend.mu.Lock()
s.backend.success++
s.backend.mu.Unlock()
return nil
})
}
func (s *smtpSession) Reset() {
s.mu.Lock()
s.from = ""
s.to = ""
s.topic = ""
s.mu.Unlock()
}
func (s *smtpSession) Logout() error {
return nil
}
func (s *smtpSession) withFailCount(fn func() error) error {
err := fn()
s.backend.mu.Lock()
defer s.backend.mu.Unlock()
if err != nil {
s.backend.failure++
}
return err
}