diff --git a/bot/command.go b/bot/command.go index 965017e..cf3ce73 100644 --- a/bot/command.go +++ b/bot/command.go @@ -438,7 +438,7 @@ func (b *Bot) runSend(ctx context.Context) { from := mailbox + "@" + domain ID := email.MessageID(evt.ID, domain) for _, to := range tos { - eml := email.New(ID, "", " "+ID, subject, from, to, body, htmlBody, nil) + eml := email.New(ID, "", " "+ID, subject, from, to, to, "", body, htmlBody, nil) data := eml.Compose(b.getBotSettings().DKIMPrivateKey()) if data == "" { b.SendError(ctx, evt.RoomID, "email body is empty") diff --git a/bot/email.go b/bot/email.go index ce9fec5..d744587 100644 --- a/bot/email.go +++ b/bot/email.go @@ -155,13 +155,8 @@ func (b *Bot) SendEmailReply(ctx context.Context) { b.lock(evt.RoomID.String()) defer b.unlock(evt.RoomID.String()) - fromMailbox := mailbox + "@" + domain meta := b.getParentEmail(evt, domain) - // when email was sent from matrix and reply was sent from matrix again - if fromMailbox != meta.From { - meta.To = meta.From - } - meta.From = fromMailbox + meta.fixtofrom(mailbox, domain, b.domains) if meta.To == "" { b.Error(ctx, evt.RoomID, "cannot find parent email and continue the thread. Please, start a new email thread") @@ -181,7 +176,7 @@ func (b *Bot) SendEmailReply(ctx context.Context) { meta.MessageID = email.MessageID(evt.ID, domain) meta.References = meta.References + " " + meta.MessageID b.log.Debug("send email reply: %+v", meta) - eml := email.New(meta.MessageID, meta.InReplyTo, meta.References, meta.Subject, meta.From, meta.To, body, htmlBody, nil) + eml := email.New(meta.MessageID, meta.InReplyTo, meta.References, meta.Subject, meta.From, meta.To, meta.RcptTo, meta.CC, body, htmlBody, nil) data := eml.Compose(b.getBotSettings().DKIMPrivateKey()) if data == "" { b.SendError(ctx, evt.RoomID, "email body is empty") @@ -208,11 +203,45 @@ type parentEmail struct { ThreadID id.EventID From string To string + RcptTo string + CC string InReplyTo string References string Subject string } +// fixtofrom attempts to "fix" or rather reverse the To, From and CC headers +// of parent email by using parent email as metadata source for a new email +// that will be sent from postmoogle. +// To do so, we need to reverse From and To headers, but Cc should be adjusted as well, +// thus that hacky workaround below: +func (e *parentEmail) fixtofrom(newSenderMailbox string, preferredDomain string, domains []string) { + newSenders := make(map[string]struct{}, len(domains)) + newSenderPref := newSenderMailbox + "@" + preferredDomain + for _, domain := range domains { + sender := newSenderMailbox + "@" + domain + newSenders[sender] = struct{}{} + } + + originalFrom := e.From + // reverse From if needed + _, ok := newSenders[e.From] + if !ok { + e.From = newSenderPref + } + // reverse To if needed + _, ok = newSenders[e.To] + if ok { + e.To = originalFrom + } + // replace previous recipient of the email which is sender now with the original From + for newSender := range newSenders { + if strings.Contains(e.CC, newSender) { + e.CC = strings.ReplaceAll(e.CC, newSender, originalFrom) + } + } +} + func (b *Bot) getParentEvent(evt *event.Event) (id.EventID, *event.Event) { content := evt.Content.AsMessage() threadID := utils.EventParent(evt.ID, content) @@ -249,8 +278,8 @@ func (b *Bot) getParentEvent(evt *event.Event) (id.EventID, *event.Event) { return threadID, decrypted } -func (b *Bot) getParentEmail(evt *event.Event, domain string) parentEmail { - var parent parentEmail +func (b *Bot) getParentEmail(evt *event.Event, domain string) *parentEmail { + parent := &parentEmail{} threadID, parentEvt := b.getParentEvent(evt) parent.ThreadID = threadID if parentEvt == nil { @@ -263,6 +292,8 @@ func (b *Bot) getParentEmail(evt *event.Event, domain string) parentEmail { parent.MessageID = email.MessageID(parentEvt.ID, domain) parent.From = utils.EventField[string](&parentEvt.Content, eventFromKey) parent.To = utils.EventField[string](&parentEvt.Content, eventToKey) + parent.CC = utils.EventField[string](&parentEvt.Content, eventCcKey) + parent.RcptTo = utils.EventField[string](&parentEvt.Content, eventRcptToKey) parent.InReplyTo = utils.EventField[string](&parentEvt.Content, eventMessageIDkey) parent.References = utils.EventField[string](&parentEvt.Content, eventReferencesKey) if parent.InReplyTo == "" { diff --git a/email/email.go b/email/email.go index 026d76f..4d503ae 100644 --- a/email/email.go +++ b/email/email.go @@ -32,7 +32,7 @@ type Email struct { } // New constructs Email object -func New(messageID, inReplyTo, references, subject, from, to, text, html string, files []*utils.File) *Email { +func New(messageID, inReplyTo, references, subject, from, to, rcptto, cc, text, html string, files []*utils.File) *Email { email := &Email{ Date: dateNow(), MessageID: messageID, @@ -40,7 +40,8 @@ func New(messageID, inReplyTo, references, subject, from, to, text, html string, References: references, From: from, To: to, - RcptTo: to, + CC: cc, + RcptTo: rcptto, Subject: subject, Text: text, HTML: html,