initial cc support

This commit is contained in:
Aine
2022-11-19 16:41:53 +02:00
parent ad83eab930
commit 9e532a6007
5 changed files with 84 additions and 48 deletions

View File

@@ -25,8 +25,10 @@ const (
eventReferencesKey = "cc.etke.postmoogle.references" eventReferencesKey = "cc.etke.postmoogle.references"
eventInReplyToKey = "cc.etke.postmoogle.inReplyTo" eventInReplyToKey = "cc.etke.postmoogle.inReplyTo"
eventSubjectKey = "cc.etke.postmoogle.subject" eventSubjectKey = "cc.etke.postmoogle.subject"
eventRcptToKey = "cc.etke.postmoogle.rcptTo"
eventFromKey = "cc.etke.postmoogle.from" eventFromKey = "cc.etke.postmoogle.from"
eventToKey = "cc.etke.postmoogle.to" eventToKey = "cc.etke.postmoogle.to"
eventCcKey = "cc.etke.postmoogle.cc"
) )
// SetSendmail sets mail sending func to the bot // SetSendmail sets mail sending func to the bot

View File

@@ -152,7 +152,9 @@ func (s roomSettings) ContentOptions() *utils.ContentOptions {
Threads: !s.NoThreads(), Threads: !s.NoThreads(),
ToKey: eventToKey, ToKey: eventToKey,
CcKey: eventCcKey,
FromKey: eventFromKey, FromKey: eventFromKey,
RcptToKey: eventRcptToKey,
SubjectKey: eventSubjectKey, SubjectKey: eventSubjectKey,
MessageIDKey: eventMessageIDkey, MessageIDKey: eventMessageIDkey,
InReplyToKey: eventInReplyToKey, InReplyToKey: eventInReplyToKey,

View File

@@ -60,6 +60,7 @@ func (m *mailServer) Login(state *smtp.ConnectionState, username, password strin
from: username, from: username,
log: m.log, log: m.log,
domains: m.domains, domains: m.domains,
tos: []string{},
}, nil }, nil
} }
@@ -80,6 +81,7 @@ func (m *mailServer) AnonymousLogin(state *smtp.ConnectionState) (smtp.Session,
log: m.log, log: m.log,
domains: m.domains, domains: m.domains,
addr: state.RemoteAddr, addr: state.RemoteAddr,
tos: []string{},
}, nil }, nil
} }

View File

@@ -28,7 +28,7 @@ type incomingSession struct {
ctx context.Context ctx context.Context
addr net.Addr addr net.Addr
to string tos []string
from string from string
} }
@@ -46,7 +46,7 @@ func (s *incomingSession) Mail(from string, opts smtp.MailOptions) error {
func (s *incomingSession) Rcpt(to string) error { func (s *incomingSession) Rcpt(to string) error {
sentry.GetHubFromContext(s.ctx).Scope().SetTag("to", to) sentry.GetHubFromContext(s.ctx).Scope().SetTag("to", to)
s.to = to s.tos = append(s.tos, to)
var domainok bool var domainok bool
for _, domain := range s.domains { for _, domain := range s.domains {
if utils.Hostname(to) == domain { if utils.Hostname(to) == domain {
@@ -66,7 +66,7 @@ func (s *incomingSession) Rcpt(to string) error {
} }
validations := s.getFilters(roomID) validations := s.getFilters(roomID)
if !validateEmail(s.from, s.to, s.log, validations) { if !validateEmail(s.from, to, s.log, validations) {
s.ban(s.addr) s.ban(s.addr)
return ErrBanned return ErrBanned
} }
@@ -89,20 +89,15 @@ func (s *incomingSession) Data(r io.Reader) error {
return err return err
} }
files := parseAttachments(eml.Attachments, s.log) email := utils.FromEnvelope(s.tos[0], eml)
for _, to := range s.tos {
email := utils.NewEmail( email.RcptTo = to
eml.GetHeader("Message-Id"), err := s.receiveEmail(s.ctx, email)
eml.GetHeader("In-Reply-To"), if err != nil {
eml.GetHeader("References"), return err
eml.GetHeader("Subject"), }
s.from, }
s.to, return nil
eml.Text,
eml.HTML,
files)
return s.receiveEmail(s.ctx, email)
} }
func (s *incomingSession) Reset() {} func (s *incomingSession) Reset() {}
func (s *incomingSession) Logout() error { return nil } func (s *incomingSession) Logout() error { return nil }
@@ -115,7 +110,7 @@ type outgoingSession struct {
domains []string domains []string
ctx context.Context ctx context.Context
to string tos []string
from string from string
} }
@@ -129,7 +124,7 @@ func (s *outgoingSession) Mail(from string, opts smtp.MailOptions) error {
func (s *outgoingSession) Rcpt(to string) error { func (s *outgoingSession) Rcpt(to string) error {
sentry.GetHubFromContext(s.ctx).Scope().SetTag("to", to) sentry.GetHubFromContext(s.ctx).Scope().SetTag("to", to)
s.to = to s.tos = append(s.tos, to)
s.log.Debug("mail to %s", to) s.log.Debug("mail to %s", to)
return nil return nil
@@ -141,21 +136,16 @@ func (s *outgoingSession) Data(r io.Reader) error {
if err != nil { if err != nil {
return err return err
} }
email := utils.FromEnvelope(s.tos[0], eml)
for _, to := range s.tos {
email.RcptTo = to
err := s.sendmail(email.From, to, email.Compose(s.privkey))
if err != nil {
return err
}
}
files := parseAttachments(eml.Attachments, s.log) return nil
email := utils.NewEmail(
eml.GetHeader("Message-Id"),
eml.GetHeader("In-Reply-To"),
eml.GetHeader("References"),
eml.GetHeader("Subject"),
s.from,
s.to,
eml.Text,
eml.HTML,
files)
return s.sendmail(email.From, email.To, email.Compose(s.privkey))
} }
func (s *outgoingSession) Reset() {} func (s *outgoingSession) Reset() {}
func (s *outgoingSession) Logout() error { return nil } func (s *outgoingSession) Logout() error { return nil }
@@ -170,16 +160,3 @@ func validateEmail(from, to string, log *logger.Logger, options utils.IncomingFi
return v.Email(from) return v.Email(from)
} }
func parseAttachments(parts []*enmime.Part, log *logger.Logger) []*utils.File {
files := make([]*utils.File, 0, len(parts))
for _, attachment := range parts {
for _, err := range attachment.Errors {
log.Warn("attachment error: %v", err)
}
file := utils.NewFile(attachment.FileName, attachment.Content)
files = append(files, file)
}
return files
}

View File

@@ -34,6 +34,8 @@ type Email struct {
References string References string
From string From string
To string To string
RcptTo string
CC string
Subject string Subject string
Text string Text string
HTML string HTML string
@@ -56,6 +58,8 @@ type ContentOptions struct {
SubjectKey string SubjectKey string
FromKey string FromKey string
ToKey string ToKey string
CcKey string
RcptToKey string
} }
// AddressValid checks if email address is valid // AddressValid checks if email address is valid
@@ -64,6 +68,16 @@ func AddressValid(email string) bool {
return err == nil return err == nil
} }
// EmailDate returns Date in RFC1123 with numeric timezone
func EmailDate(original ...time.Time) string {
now := time.Now().UTC()
if len(original) > 0 && !original[0].IsZero() {
now = original[0]
}
return now.Format(time.RFC1123Z)
}
// MessageID generates email Message-Id from matrix event ID // MessageID generates email Message-Id from matrix event ID
func MessageID(eventID id.EventID, domain string) string { func MessageID(eventID id.EventID, domain string) string {
return fmt.Sprintf("<%s@%s>", eventID, domain) return fmt.Sprintf("<%s@%s>", eventID, domain)
@@ -72,12 +86,13 @@ func MessageID(eventID id.EventID, domain string) string {
// NewEmail constructs Email object // NewEmail constructs Email object
func NewEmail(messageID, inReplyTo, references, subject, from, to, text, html string, files []*File) *Email { func NewEmail(messageID, inReplyTo, references, subject, from, to, text, html string, files []*File) *Email {
email := &Email{ email := &Email{
Date: time.Now().UTC().Format(time.RFC1123Z), Date: EmailDate(),
MessageID: messageID, MessageID: messageID,
InReplyTo: inReplyTo, InReplyTo: inReplyTo,
References: references, References: references,
From: from, From: from,
To: to, To: to,
RcptTo: to,
Subject: subject, Subject: subject,
Text: text, Text: text,
HTML: html, HTML: html,
@@ -92,10 +107,46 @@ func NewEmail(messageID, inReplyTo, references, subject, from, to, text, html st
return email return email
} }
func FromEnvelope(rcptto string, eml *enmime.Envelope) *Email {
datetime, _ := eml.Date() //nolint:errcheck // handled in EmailDate()
date := EmailDate(datetime)
var html string
if eml.HTML != "" {
html = styleRegex.ReplaceAllString(eml.HTML, "")
}
files := make([]*File, 0, len(eml.Attachments))
for _, attachment := range eml.Attachments {
for _, err := range attachment.Errors {
log.Warn("attachment error: %v", err)
}
file := NewFile(attachment.FileName, attachment.Content)
files = append(files, file)
}
email := &Email{
Date: date,
MessageID: eml.GetHeader("Message-Id"),
InReplyTo: eml.GetHeader("In-Reply-To"),
References: eml.GetHeader("References"),
From: eml.GetHeader("From"),
To: eml.GetHeader("To"),
RcptTo: rcptto,
CC: eml.GetHeader("Cc"),
Subject: eml.GetHeader("Subject"),
Text: eml.Text,
HTML: html,
Files: files,
}
return email
}
// Mailbox returns postmoogle's mailbox, parsing it from FROM (if incoming=false) or TO (incoming=true) // Mailbox returns postmoogle's mailbox, parsing it from FROM (if incoming=false) or TO (incoming=true)
func (e *Email) Mailbox(incoming bool) string { func (e *Email) Mailbox(incoming bool) string {
if incoming { if incoming {
return Mailbox(e.To) return Mailbox(e.RcptTo)
} }
return Mailbox(e.From) return Mailbox(e.From)
} }
@@ -133,8 +184,10 @@ func (e *Email) Content(threadID id.EventID, options *ContentOptions) *event.Con
options.InReplyToKey: e.InReplyTo, options.InReplyToKey: e.InReplyTo,
options.ReferencesKey: e.References, options.ReferencesKey: e.References,
options.SubjectKey: e.Subject, options.SubjectKey: e.Subject,
options.RcptToKey: e.RcptTo,
options.FromKey: e.From, options.FromKey: e.From,
options.ToKey: e.To, options.ToKey: e.To,
options.CcKey: e.CC,
}, },
Parsed: &parsed, Parsed: &parsed,
} }