126 lines
2.6 KiB
Go
126 lines
2.6 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"io"
|
||
|
"log"
|
||
|
"net/http"
|
||
|
"regexp"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"github.com/emersion/go-message/mail"
|
||
|
"github.com/emersion/go-smtp"
|
||
|
|
||
|
// Support decoding all charsets
|
||
|
// https://github.com/emersion/go-message/issues/148
|
||
|
_ "github.com/emersion/go-message/charset"
|
||
|
)
|
||
|
|
||
|
var errError = errors.New("error")
|
||
|
|
||
|
// [^\s"] breaks on both whitespace and quote, ensuring it works for both plaintext and HTML
|
||
|
var re = regexp.MustCompile(`https://api\.dr\.dk/login/interactions/email/confirmation[^\s"]+`)
|
||
|
|
||
|
type Backend struct{}
|
||
|
|
||
|
func (bkd *Backend) NewSession(c *smtp.Conn) (smtp.Session, error) {
|
||
|
return &Session{}, nil
|
||
|
}
|
||
|
|
||
|
type Session struct{}
|
||
|
|
||
|
func (s *Session) Mail(from string, opts *smtp.MailOptions) error {
|
||
|
// TODO: ever heard of DMARC?
|
||
|
if !strings.HasSuffix(from, "dr.dk") {
|
||
|
return errError
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *Session) Rcpt(to string, opts *smtp.RcptOptions) error {
|
||
|
if !strings.HasSuffix(to, "@sortseer.dk") {
|
||
|
return errError
|
||
|
}
|
||
|
log.Println("RCPT:", to)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *Session) Data(r io.Reader) error {
|
||
|
// https://godocs.io/github.com/emersion/go-message/mail#example-Reader
|
||
|
// Create a new mail reader
|
||
|
reader, err := mail.CreateReader(r)
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
// Read each mail part
|
||
|
for {
|
||
|
part, err := reader.NextPart()
|
||
|
if err != nil {
|
||
|
if err == io.EOF {
|
||
|
break
|
||
|
}
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
// Skip e.g. attachments
|
||
|
_, isInline := part.Header.(*mail.InlineHeader)
|
||
|
if !isInline {
|
||
|
continue
|
||
|
}
|
||
|
// Read part body
|
||
|
partBody, err := io.ReadAll(part.Body)
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
// Find confirmation link
|
||
|
match := re.Find(partBody)
|
||
|
if match == nil {
|
||
|
continue
|
||
|
}
|
||
|
link := string(match)
|
||
|
log.Println(link)
|
||
|
// Open link
|
||
|
response, err := http.Get(link)
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
// Check response
|
||
|
responseBody, err := io.ReadAll(response.Body)
|
||
|
response.Body.Close()
|
||
|
log.Println(response.StatusCode)
|
||
|
if response.StatusCode != 200 {
|
||
|
log.Println(string(responseBody))
|
||
|
}
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
break
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *Session) Reset() {}
|
||
|
|
||
|
func (s *Session) Logout() error {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
// https://pkg.go.dev/github.com/emersion/go-smtp#example-Server
|
||
|
backend := &Backend{}
|
||
|
|
||
|
server := smtp.NewServer(backend)
|
||
|
server.Addr = "0.0.0.0:25"
|
||
|
server.Domain = "sortseer.dk"
|
||
|
server.WriteTimeout = 5 * time.Second
|
||
|
server.ReadTimeout = 5 * time.Second
|
||
|
server.MaxMessageBytes = 1024 * 1024
|
||
|
server.MaxRecipients = 5
|
||
|
server.AllowInsecureAuth = true
|
||
|
|
||
|
log.Println("Listening on", server.Addr)
|
||
|
if err := server.ListenAndServe(); err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
}
|