Skip to content

Commit

Permalink
smtp server support improvements, added stuff to readme
Browse files Browse the repository at this point in the history
This allows multiple "header" url parameters to be sent.
  • Loading branch information
martinrode committed Jul 12, 2024
1 parent 4bc7e61 commit 26ff1af
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 44 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2964,6 +2964,8 @@ available as JSON in the following schema:
}
```

> You can filter messages by passing one of more query parameters `header`. `header` can either be a JSON array of strings, or just a string. The filter checks that all headers (regexp format) match headers of the filtered email.
Headers that were encoded according to RFC2047 are decoded first.

#### /smtp/$idx
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ require (
github.com/moul/http2curl v1.0.0
github.com/pkg/errors v0.9.1
github.com/programmfabrik/go-test-utils v0.0.0-20191114143449-b8e16b04adb1
github.com/programmfabrik/golib v0.0.0-20240226091422-733aede66819
github.com/programmfabrik/golib v0.0.0-20240701125551-843bc5e3be55
github.com/sergi/go-diff v1.3.1
github.com/sirupsen/logrus v1.9.3
github.com/spf13/afero v1.9.5
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ github.com/programmfabrik/go-test-utils v0.0.0-20191114143449-b8e16b04adb1 h1:Nb
github.com/programmfabrik/go-test-utils v0.0.0-20191114143449-b8e16b04adb1/go.mod h1:6Tg7G+t9KYiFa0sU8PpISt9RUgIpgrEI+tXvWz3tSIU=
github.com/programmfabrik/golib v0.0.0-20240226091422-733aede66819 h1:lJ+a0MLo4Dn2UTF0Q/nh9msLqP8MaNEL/RbJLop022g=
github.com/programmfabrik/golib v0.0.0-20240226091422-733aede66819/go.mod h1:qb4pSUhPsZ/UfvM/MBNwKHb6W7xL85uSi4od9emNHHw=
github.com/programmfabrik/golib v0.0.0-20240701125551-843bc5e3be55 h1:VBYGpSvjwHSa5ARrs6uPlUOJF1+n6rFWn49+++h20IU=
github.com/programmfabrik/golib v0.0.0-20240701125551-843bc5e3be55/go.mod h1:qb4pSUhPsZ/UfvM/MBNwKHb6W7xL85uSi4od9emNHHw=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
Expand Down
2 changes: 1 addition & 1 deletion http_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ func logH(skipLog bool, next http.Handler) http.Handler {
return next
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logrus.Debugf("http-server: %s: %q", r.Method, r.URL)
// logrus.Debugf("http-server: %s: %q", r.Method, r.URL)
next.ServeHTTP(w, r)
})
}
2 changes: 1 addition & 1 deletion internal/handlerutil/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func LogH(skipLogs bool, next http.Handler) http.Handler {
return next
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logrus.Debugf("http-server: %s: %q", r.Method, r.URL)
// logrus.Debugf("http-server: %s: %q", r.Method, r.URL)
next.ServeHTTP(w, r)
})
}
Expand Down
69 changes: 30 additions & 39 deletions internal/smtp/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"io"
"net/http"
"net/mail"
"net/url"
"path"
"regexp"
"strconv"
Expand All @@ -17,6 +18,7 @@ import (

"github.com/Masterminds/sprig/v3"
"github.com/programmfabrik/apitest/internal/handlerutil"
"github.com/programmfabrik/golib"
"github.com/sirupsen/logrus"
)

Expand Down Expand Up @@ -220,29 +222,22 @@ func (h *smtpHTTPHandler) handleGUIIndex(w http.ResponseWriter, r *http.Request)

func (h *smtpHTTPHandler) handleGUIMessage(w http.ResponseWriter, r *http.Request, msg *ReceivedMessage) {
metadata := buildMessageFullMeta(msg)
metadataJson, err := json.MarshalIndent(metadata, "", " ")
if err != nil {
handlerutil.RespondWithErr(
w, http.StatusInternalServerError,
fmt.Errorf("could not build metadata JSON: %w", err),
)
return
}
metadataJson := golib.JsonStringIndent(metadata, "", " ")

w.Header().Set("Content-Type", "text/html; charset=utf-8")

err = guiMessageTemplate.Execute(w, map[string]any{
err := guiMessageTemplate.Execute(w, map[string]any{
"prefix": h.prefix,
"metadata": metadata,
"metadataJson": string(metadataJson),
"metadataJson": metadataJson,
})
if err != nil {
logrus.Error("error rendering GUI Message:", err)
}
}

func (h *smtpHTTPHandler) handleMessageIndex(w http.ResponseWriter, r *http.Request) {
headerSearchRxs, err := extractSearchRegexes(w, r.URL.Query(), "header")
headerSearchRxs, err := extractSearchRegexes(r.URL.Query(), "header")
if err != nil {
handlerutil.RespondWithErr(w, http.StatusBadRequest, err)
return
Expand Down Expand Up @@ -276,7 +271,7 @@ func (h *smtpHTTPHandler) handleMessageRaw(w http.ResponseWriter, r *http.Reques
}

func (h *smtpHTTPHandler) handleMultipartIndex(w http.ResponseWriter, r *http.Request, c *ReceivedContent) {
headerSearchRxs, err := extractSearchRegexes(w, r.URL.Query(), "header")
headerSearchRxs, err := extractSearchRegexes(r.URL.Query(), "header")
if err != nil {
handlerutil.RespondWithErr(w, http.StatusBadRequest, err)
return
Expand Down Expand Up @@ -478,39 +473,35 @@ func buildMultipartMeta(part *ReceivedPart) map[string]any {
// extractSearchRegexes tries to extract the regular expression(s) from the
// referenced query parameter. If no query parameter is given and otherwise
// no error has occurred, this function returns no error.
func extractSearchRegexes(
w http.ResponseWriter, queryParams map[string][]string, paramName string,
) ([]*regexp.Regexp, error) {
filteredParams, ok := queryParams[paramName]
if ok {
if len(filteredParams) != 1 {
return nil, fmt.Errorf(
"expected 1 %q query parameter, got %d (use JSON array for multiple queries)",
paramName, len(filteredParams),
)
func extractSearchRegexes(qp url.Values, paramName string) (rgs []*regexp.Regexp, err error) {
if !qp.Has(paramName) {
return nil, nil
}
defer func() {
if err == nil {
println(fmt.Sprintf("%v", rgs))
}
}()

sp := []string{}
for _, v := range qp[paramName] {
var searchParams []string
err := json.Unmarshal([]byte(filteredParams[0]), &searchParams)
if err != nil {
searchParams = []string{filteredParams[0]}
err := json.Unmarshal([]byte(v), &searchParams)
if err == nil {
sp = append(sp, searchParams...)
} else {
// this is not a JSON string array, assume string
sp = append(sp, v)
}
}

out := make([]*regexp.Regexp, len(searchParams))

for i, p := range searchParams {
re, err := regexp.Compile(p)
if err != nil {
return nil, fmt.Errorf(
"could not compile %q regex %q: %w", paramName, p, err,
)
}

out[i] = re
for _, p := range sp {
re, err := regexp.Compile(p)
if err != nil {
return nil, fmt.Errorf("could not compile %q regex %q: %w", paramName, p, err)
}

return out, nil
rgs = append(rgs, re)
}

return nil, nil
return rgs, nil
}
3 changes: 1 addition & 2 deletions internal/smtp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,6 @@ func (s *session) Data(r io.Reader) error {
idx := len(s.server.receivedMessages)
now := s.server.clock()

logrus.Infof("SMTP: Receiving message from %s to %v at %v", s.from, s.rcptTo, now)
msg, err := NewReceivedMessage(
idx, s.from, s.rcptTo, rawData, now, s.server.maxMessageSize,
)
Expand All @@ -150,7 +149,7 @@ func (s *session) Data(r io.Reader) error {
logrus.Error("SMTP:", errWrapped) // this is logged in our server
return errWrapped // this is returned via SMTP
}
logrus.Infof("SMTP: Reception successful")
logrus.Debugf("SMTP: Message from %s to %v at %v", s.from, s.rcptTo, now.Format(time.DateTime))

s.server.receivedMessages = append(s.server.receivedMessages, msg)

Expand Down

0 comments on commit 26ff1af

Please sign in to comment.