From cfa9f6110142fb284f19bf705e081db3288a2f48 Mon Sep 17 00:00:00 2001 From: Lucas Hinderberger Date: Mon, 1 Jul 2024 17:51:53 +0200 Subject: [PATCH] SMTP: ReceivedContent refactoring part 3 (refactoring search to use ReceivedContent) --- internal/smtp/http.go | 19 ++++++++----------- internal/smtp/message.go | 34 ---------------------------------- internal/smtp/search.go | 20 ++++++++++++++------ internal/smtp/server.go | 32 -------------------------------- internal/smtp/smtp_test.go | 4 ++-- 5 files changed, 24 insertions(+), 85 deletions(-) diff --git a/internal/smtp/http.go b/internal/smtp/http.go index ccb386c..3907014 100644 --- a/internal/smtp/http.go +++ b/internal/smtp/http.go @@ -116,17 +116,15 @@ func (h *smtpHTTPHandler) handleGUI(w http.ResponseWriter, r *http.Request) { } func (h *smtpHTTPHandler) handleMessageIndex(w http.ResponseWriter, r *http.Request) { - var receivedMessages []*ReceivedMessage - headerSearchRgx, err := extractSearchRegex(w, r.URL.Query(), "header") if err != nil { handlerutil.RespondWithErr(w, http.StatusBadRequest, err) return } - if headerSearchRgx == nil { - receivedMessages = h.server.ReceivedMessages() - } else { - receivedMessages = h.server.SearchByHeader(headerSearchRgx) + + receivedMessages := h.server.ReceivedMessages() + if headerSearchRgx != nil { + receivedMessages = SearchByHeader(receivedMessages, headerSearchRgx) } messagesOut := make([]any, 0) @@ -188,16 +186,15 @@ func (h *smtpHTTPHandler) handleMultipartIndex(w http.ResponseWriter, r *http.Re return } - var multiparts []*ReceivedPart headerSearchRgx, err := extractSearchRegex(w, r.URL.Query(), "header") if err != nil { handlerutil.RespondWithErr(w, http.StatusBadRequest, err) return } - if headerSearchRgx == nil { - multiparts = msg.Content().Multiparts() - } else { - multiparts = msg.SearchPartsByHeader(headerSearchRgx) + + multiparts := msg.Content().Multiparts() + if headerSearchRgx != nil { + multiparts = SearchByHeader(multiparts, headerSearchRgx) } multipartsOut := make([]any, 0) diff --git a/internal/smtp/message.go b/internal/smtp/message.go index 5a61ae2..c3b6eba 100644 --- a/internal/smtp/message.go +++ b/internal/smtp/message.go @@ -10,7 +10,6 @@ import ( "mime/multipart" "mime/quotedprintable" "net/mail" - "regexp" "strings" "time" @@ -92,39 +91,6 @@ func NewReceivedMessage( return msg, nil } -// SearchPartsByHeader returns the list of all received multiparts that -// have at least one header matching the given regular expression. -// -// For details on how the matching is performed, please refer to the -// documentation for Server.SearchByHeader. -// -// If the message is not a multipart message, this returns nil. -// If no matching multiparts are found, this may return nil or an empty -// list. -func (m *ReceivedMessage) SearchPartsByHeader(re *regexp.Regexp) []*ReceivedPart { - // TODO: Somehow unify with Server.SearchByHeader based on ReceivedContent - - if !m.content.IsMultipart() { - return nil - } - - multiparts := m.content.Multiparts() - - headerIdxList := make([]map[string][]string, len(multiparts)) - for i, v := range multiparts { - headerIdxList[i] = v.Content().Headers() - } - - foundIndices := searchByHeaderCommon(headerIdxList, re) - - results := make([]*ReceivedPart, 0, len(foundIndices)) - for _, idx := range foundIndices { - results = append(results, multiparts[idx]) - } - - return results -} - // NewReceivedPart parses a MIME multipart part into a ReceivedPart struct. // // maxMessageSize is passed through to NewReceivedContent (see its documentation for details). diff --git a/internal/smtp/search.go b/internal/smtp/search.go index 38b27f5..7446e42 100644 --- a/internal/smtp/search.go +++ b/internal/smtp/search.go @@ -5,16 +5,24 @@ import ( "regexp" ) -func searchByHeaderCommon(headerIdxList []map[string][]string, re *regexp.Regexp) []int { - result := make([]int, 0, len(headerIdxList)) +// SearchByHeader returns the list of all given ContentHavers that +// have at least one header matching the given regular expression. +// +// Note that the regex is performed for each header value individually, +// including for multi-value headers. The header value is first serialized +// by concatenating it after the header name, colon and space. It is not +// being encoded as if for transport (e.g. quoted-printable), +// but concatenated as-is. +func SearchByHeader[T ContentHaver](haystack []T, re *regexp.Regexp) []T { + out := make([]T, 0, len(haystack)) - for idx, headers := range headerIdxList { - if anyHeaderMatches(headers, re) { - result = append(result, idx) + for _, c := range haystack { + if anyHeaderMatches(c.Content().Headers(), re) { + out = append(out, c) } } - return result + return out } func anyHeaderMatches(headers map[string][]string, re *regexp.Regexp) bool { diff --git a/internal/smtp/server.go b/internal/smtp/server.go index eeb3791..4da0f0d 100644 --- a/internal/smtp/server.go +++ b/internal/smtp/server.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "io" - "regexp" "sync" "time" @@ -110,37 +109,6 @@ func (s *Server) ReceivedMessages() []*ReceivedMessage { return view } -// SearchByHeader returns the list of all received messages that have at -// least one header matching the given regular expression. -// -// Note that the regex is performed for each header value individually, -// including for multi-value headers. The header value is first serialized -// by concatenating it after the header name, colon and space. It is not -// being encoded as if for transport (e.g. quoted-printable), -// but concatenated as-is. -func (s *Server) SearchByHeader(re *regexp.Regexp) []*ReceivedMessage { - // TODO: Somehow unify with ReceivedMessage.SearchPartsByHeader based on ReceivedContent - - s.mutex.RLock() - defer s.mutex.RUnlock() - - receivedMessages := s.ReceivedMessages() - - headerIdxList := make([]map[string][]string, len(receivedMessages)) - for i, v := range receivedMessages { - headerIdxList[i] = v.Content().Headers() - } - - foundIndices := searchByHeaderCommon(headerIdxList, re) - - results := make([]*ReceivedMessage, 0, len(foundIndices)) - for _, idx := range foundIndices { - results = append(results, receivedMessages[idx]) - } - - return results -} - func newSession(server *Server, c *smtp.Conn) (smtp.Session, error) { return &session{ server: server, diff --git a/internal/smtp/smtp_test.go b/internal/smtp/smtp_test.go index 93372f8..2e4c66e 100644 --- a/internal/smtp/smtp_test.go +++ b/internal/smtp/smtp_test.go @@ -91,7 +91,7 @@ func TestMessageSearch(t *testing.T) { query := testCase.queries[j] t.Run(query, func(t *testing.T) { re := regexp.MustCompile(query) - actual := server.SearchByHeader(re) + actual := SearchByHeader(server.ReceivedMessages(), re) actualIndices := make([]int, len(actual)) for ai, av := range actual { @@ -158,7 +158,7 @@ func TestMultipartSearch(t *testing.T) { msg, err := server.ReceivedMessage(8) require.NoError(t, err) - actual := msg.SearchPartsByHeader(re) + actual := SearchByHeader(msg.Content().Multiparts(), re) actualIndices := make([]int, len(actual)) for ai, av := range actual {