From f256d205ed933769a1018ed428903d2f95f9d121 Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Wed, 18 Oct 2023 17:40:40 +1300 Subject: [PATCH] Feature: Reset message date on release (#194) When releasing a message the date header is now updated with the current date & time. --- internal/tools/message.go | 46 ++++++++++++++++++++++++++++++++++++--- server/apiv1/api.go | 18 ++++++++++++--- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/internal/tools/message.go b/internal/tools/message.go index b28cc1893..02d0ff86c 100644 --- a/internal/tools/message.go +++ b/internal/tools/message.go @@ -11,8 +11,7 @@ import ( ) // RemoveMessageHeaders scans a message for headers, if found them removes them. -// It will only remove a single instance of any header, and is intended to remove -// Bcc & Message-Id. +// It will only remove a single instance of any given message header. func RemoveMessageHeaders(msg []byte, headers []string) ([]byte, error) { reader := bytes.NewReader(msg) m, err := mail.ReadMessage(reader) @@ -49,7 +48,7 @@ func RemoveMessageHeaders(msg []byte, headers []string) ([]byte, error) { } if len(hdr) > 0 { - logger.Log().Debugf("[release] removing %s header", hdr) + logger.Log().Debugf("[release] removed %s header", hdr) msg = bytes.Replace(msg, hdr, []byte(""), 1) } } @@ -57,3 +56,44 @@ func RemoveMessageHeaders(msg []byte, headers []string) ([]byte, error) { return msg, nil } + +// UpdateMessageHeader scans a message for a header and updates its value if found. +func UpdateMessageHeader(msg []byte, header, value string) ([]byte, error) { + reader := bytes.NewReader(msg) + m, err := mail.ReadMessage(reader) + if err != nil { + return nil, err + } + + if m.Header.Get(header) != "" { + reBlank := regexp.MustCompile(`^\s+`) + reHdr := regexp.MustCompile(`(?i)^` + regexp.QuoteMeta(header+":")) + + scanner := bufio.NewScanner(bytes.NewReader(msg)) + found := false + hdr := []byte("") + for scanner.Scan() { + line := scanner.Bytes() + if !found && reHdr.Match(line) { + // add the first line starting with
: + hdr = append(hdr, line...) + hdr = append(hdr, []byte("\r\n")...) + found = true + } else if found && reBlank.Match(line) { + // add any following lines starting with a whitespace (tab or space) + hdr = append(hdr, line...) + hdr = append(hdr, []byte("\r\n")...) + } else if found { + // stop scanning, we have the full
+ break + } + } + + if len(hdr) > 0 { + logger.Log().Debugf("[release] replaced %s header", hdr) + msg = bytes.Replace(msg, hdr, []byte(header+": "+value+"\r\n"), 1) + } + } + + return msg, nil +} diff --git a/server/apiv1/api.go b/server/apiv1/api.go index 5ad4951f3..1afd70342 100644 --- a/server/apiv1/api.go +++ b/server/apiv1/api.go @@ -9,6 +9,7 @@ import ( "net/mail" "strconv" "strings" + "time" "github.com/axllent/mailpit/config" "github.com/axllent/mailpit/internal/htmlcheck" @@ -632,7 +633,7 @@ func ReleaseMessage(w http.ResponseWriter, r *http.Request) { from = senders[0].Address } - msg, err = tools.RemoveMessageHeaders(msg, []string{"Bcc", "Message-Id"}) + msg, err = tools.RemoveMessageHeaders(msg, []string{"Bcc"}) if err != nil { httpError(w, err.Error()) return @@ -652,10 +653,21 @@ func ReleaseMessage(w http.ResponseWriter, r *http.Request) { from = config.SMTPRelayConfig.ReturnPath } + // update message date + msg, err = tools.UpdateMessageHeader(msg, "Date", time.Now().Format(time.RFC1123Z)) + if err != nil { + httpError(w, err.Error()) + return + } + // generate unique ID uid := uuid.New().String() + "@mailpit" - // add unique ID - msg = append([]byte("Message-Id: <"+uid+">\r\n"), msg...) + // update Message-Id with unique ID + msg, err = tools.UpdateMessageHeader(msg, "Message-Id", "<"+uid+">") + if err != nil { + httpError(w, err.Error()) + return + } if err := smtpd.Send(from, tos, msg); err != nil { logger.Log().Errorf("[smtp] error sending message: %s", err.Error())