package smtp import ( "context" "io" "strings" "github.com/emersion/go-smtp" "github.com/getsentry/sentry-go" "gitlab.com/etke.cc/go/logger" "gitlab.com/etke.cc/go/trysmtp" "gitlab.com/etke.cc/postmoogle/email" ) var ( // ErrBanned returned to banned hosts ErrBanned = &smtp.SMTPError{ Code: 554, EnhancedCode: smtp.EnhancedCode{5, 5, 4}, Message: "please, don't bother me anymore, kupo.", } // ErrNoUser returned when no such mailbox found ErrNoUser = &smtp.SMTPError{ Code: 550, EnhancedCode: smtp.EnhancedCode{5, 5, 0}, Message: "no such user here, kupo.", } ) type mailServer struct { bot matrixbot log *logger.Logger domains []string } // Login used for outgoing mail submissions only (when you use postmoogle as smtp server in your scripts) func (m *mailServer) Login(state *smtp.ConnectionState, username, password string) (smtp.Session, error) { m.log.Debug("Login state=%+v username=%+v", state, username) if m.bot.IsBanned(state.RemoteAddr) { return nil, ErrBanned } if !email.AddressValid(username) { m.log.Debug("address %s is invalid", username) m.bot.Ban(state.RemoteAddr) return nil, ErrBanned } if !m.bot.AllowAuth(username, password) { m.log.Debug("username=%s or password= is invalid", username) m.bot.Ban(state.RemoteAddr) return nil, ErrBanned } return &outgoingSession{ ctx: sentry.SetHubOnContext(context.Background(), sentry.CurrentHub().Clone()), sendmail: m.SendEmail, privkey: m.bot.GetDKIMprivkey(), from: username, log: m.log, domains: m.domains, tos: []string{}, }, nil } // AnonymousLogin used for incoming mail submissions only func (m *mailServer) AnonymousLogin(state *smtp.ConnectionState) (smtp.Session, error) { m.log.Debug("AnonymousLogin state=%+v", state) if m.bot.IsBanned(state.RemoteAddr) { return nil, ErrBanned } return &incomingSession{ ctx: sentry.SetHubOnContext(context.Background(), sentry.CurrentHub().Clone()), getRoomID: m.bot.GetMapping, getFilters: m.bot.GetIFOptions, receiveEmail: m.ReceiveEmail, ban: m.bot.Ban, greylisted: m.bot.IsGreylisted, log: m.log, domains: m.domains, addr: state.RemoteAddr, tos: []string{}, }, nil } // SendEmail to external mail server func (m *mailServer) SendEmail(from, to, data string) error { m.log.Debug("Sending email from %s to %s", from, to) conn, err := trysmtp.Connect(from, to) if err != nil { m.log.Error("cannot connect to SMTP server of %s: %v", to, err) return err } defer conn.Close() var w io.WriteCloser w, err = conn.Data() if err != nil { m.log.Error("cannot send DATA command: %v", err) return err } defer w.Close() m.log.Debug("sending DATA:\n%s", data) _, err = strings.NewReader(data).WriteTo(w) if err != nil { m.log.Debug("cannot write DATA: %v", err) return err } m.log.Debug("email has been sent") return nil } // ReceiveEmail - incoming mail into matrix room func (m *mailServer) ReceiveEmail(ctx context.Context, eml *email.Email) error { return m.bot.IncomingEmail(ctx, eml) }