1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2025-12-17 09:37:02 +00:00
Files
go-inbucket/pkg/message/manager.go
James Hillyerd 74a27875e9 message: migrate more delivery logic into manager.go (#414)
* message: migrate more delivery logic into manager.go

Signed-off-by: James Hillyerd <james@hillyerd.com>

* manager: tidy up a few things

Signed-off-by: James Hillyerd <james@hillyerd.com>

---------

Signed-off-by: James Hillyerd <james@hillyerd.com>
2023-11-01 13:54:27 -07:00

186 lines
5.0 KiB
Go

package message
import (
"bytes"
"fmt"
"io"
"net/mail"
"strings"
"time"
"github.com/inbucket/inbucket/v3/pkg/extension"
"github.com/inbucket/inbucket/v3/pkg/extension/event"
"github.com/inbucket/inbucket/v3/pkg/policy"
"github.com/inbucket/inbucket/v3/pkg/storage"
"github.com/jhillyerd/enmime"
"github.com/rs/zerolog/log"
)
// recvdTimeFmt to use in generated Received header.
const recvdTimeFmt = "Mon, 02 Jan 2006 15:04:05 -0700 (MST)"
// Manager is the interface controllers use to interact with messages.
type Manager interface {
Deliver(
from *policy.Origin,
recipients []*policy.Recipient,
recvdHeader string,
content []byte,
) error
GetMetadata(mailbox string) ([]*event.MessageMetadata, error)
GetMessage(mailbox, id string) (*Message, error)
MarkSeen(mailbox, id string) error
PurgeMessages(mailbox string) error
RemoveMessage(mailbox, id string) error
SourceReader(mailbox, id string) (io.ReadCloser, error)
MailboxForAddress(address string) (string, error)
}
// StoreManager is a message Manager backed by the storage.Store.
type StoreManager struct {
AddrPolicy *policy.Addressing
Store storage.Store
ExtHost *extension.Host
}
// Deliver submits a new message to the store.
func (s *StoreManager) Deliver(
from *policy.Origin,
recipients []*policy.Recipient,
recvdHeader string,
source []byte,
) error {
logger := log.With().Str("module", "message").Logger()
// Parse envelope headers.
header, err := enmime.DecodeHeaders(source)
if err != nil {
return err
}
fromaddr, err := enmime.ParseAddressList(header.Get("From"))
if err != nil || len(fromaddr) == 0 {
fromaddr = make([]*mail.Address, 1)
fromaddr[0] = &from.Address
}
toaddr, err := enmime.ParseAddressList(header.Get("To"))
if err != nil {
toaddr = make([]*mail.Address, len(recipients))
for i, torecip := range recipients {
toaddr[i] = &torecip.Address
}
}
now := time.Now()
tstamp := now.UTC().Format(recvdTimeFmt)
// Deliver to mailboxes.
for _, recip := range recipients {
if recip.ShouldStore() {
// Append recipient and timestamp to generated Recieved header.
recvd := fmt.Sprintf("%s for <%s>; %s\r\n", recvdHeader, recip.Address.Address, tstamp)
// Deliver message.
logger.Debug().Str("mailbox", recip.Mailbox).Msg("Delivering message")
delivery := &Delivery{
Meta: event.MessageMetadata{
Mailbox: recip.Mailbox,
From: fromaddr[0],
To: toaddr,
Date: now,
Subject: header.Get("Subject"),
},
Reader: io.MultiReader(strings.NewReader(recvd), bytes.NewReader(source)),
}
id, err := s.Store.AddMessage(delivery)
if err != nil {
logger.Error().Str("mailbox", recip.Mailbox).Err(err).Msg("Delivery failed")
return err
}
// Emit message stored event.
event := delivery.Meta
event.ID = id
s.ExtHost.Events.AfterMessageStored.Emit(&event)
}
}
return nil
}
// GetMetadata returns a slice of metadata for the specified mailbox.
func (s *StoreManager) GetMetadata(mailbox string) ([]*event.MessageMetadata, error) {
messages, err := s.Store.GetMessages(mailbox)
if err != nil {
return nil, err
}
metas := make([]*event.MessageMetadata, len(messages))
for i, sm := range messages {
metas[i] = MakeMetadata(sm)
}
return metas, nil
}
// GetMessage returns the specified message.
func (s *StoreManager) GetMessage(mailbox, id string) (*Message, error) {
sm, err := s.Store.GetMessage(mailbox, id)
if err != nil || sm == nil {
return nil, err
}
r, err := sm.Source()
if err != nil {
return nil, err
}
env, err := enmime.ReadEnvelope(r)
if err != nil {
return nil, err
}
_ = r.Close()
header := MakeMetadata(sm)
return &Message{MessageMetadata: *header, env: env}, nil
}
// MarkSeen marks the message as having been read.
func (s *StoreManager) MarkSeen(mailbox, id string) error {
log.Debug().Str("module", "manager").Str("mailbox", mailbox).Str("id", id).
Msg("Marking as seen")
return s.Store.MarkSeen(mailbox, id)
}
// PurgeMessages removes all messages from the specified mailbox.
func (s *StoreManager) PurgeMessages(mailbox string) error {
return s.Store.PurgeMessages(mailbox)
}
// RemoveMessage deletes the specified message.
func (s *StoreManager) RemoveMessage(mailbox, id string) error {
return s.Store.RemoveMessage(mailbox, id)
}
// SourceReader allows the stored message source to be read.
func (s *StoreManager) SourceReader(mailbox, id string) (io.ReadCloser, error) {
sm, err := s.Store.GetMessage(mailbox, id)
if err != nil || sm == nil {
return nil, err
}
return sm.Source()
}
// MailboxForAddress parses an email address to return the canonical mailbox name.
func (s *StoreManager) MailboxForAddress(mailbox string) (string, error) {
return s.AddrPolicy.ExtractMailbox(mailbox)
}
// MakeMetadata populates Metadata from a storage.Message.
func MakeMetadata(m storage.Message) *event.MessageMetadata {
return &event.MessageMetadata{
Mailbox: m.Mailbox(),
ID: m.ID(),
From: m.From(),
To: m.To(),
Date: m.Date(),
Subject: m.Subject(),
Size: m.Size(),
Seen: m.Seen(),
}
}