Skip to content

Commit

Permalink
SMTP: ReceivedContent refactoring part 3 (refactoring search to use R…
Browse files Browse the repository at this point in the history
…eceivedContent)
  • Loading branch information
Lucas Hinderberger committed Jul 1, 2024
1 parent ad38c5f commit cfa9f61
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 85 deletions.
19 changes: 8 additions & 11 deletions internal/smtp/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
34 changes: 0 additions & 34 deletions internal/smtp/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"mime/multipart"
"mime/quotedprintable"
"net/mail"
"regexp"
"strings"
"time"

Expand Down Expand Up @@ -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).
Expand Down
20 changes: 14 additions & 6 deletions internal/smtp/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
32 changes: 0 additions & 32 deletions internal/smtp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"fmt"
"io"
"regexp"
"sync"
"time"

Expand Down Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions internal/smtp/smtp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit cfa9f61

Please sign in to comment.