diff --git a/apidiff.sh b/apidiff.sh index 6024096a1..d30b7cbeb 100755 --- a/apidiff.sh +++ b/apidiff.sh @@ -8,20 +8,30 @@ if ! test -d tmp/mox-$prevversion; then fi (rm -r tmp/apidiff || exit 0) mkdir -p tmp/apidiff/$prevversion tmp/apidiff/next -(rm apidiff/next.txt || exit 0) -( -echo "Below are the incompatible changes between $prevversion and next, per package." -echo -) >>apidiff/next.txt +(rm apidiff/next.txt apidiff/next.txt.new 2>/dev/null || exit 0) for p in $(cat apidiff/packages.txt); do if ! test -d tmp/mox-$prevversion/$p; then continue fi (cd tmp/mox-$prevversion && apidiff -w ../apidiff/$prevversion/$p.api ./$p) apidiff -w tmp/apidiff/next/$p.api ./$p + apidiff -incompatible tmp/apidiff/$prevversion/$p.api tmp/apidiff/next/$p.api >$p.diff + if test -s $p.diff; then + ( + echo '#' $p + cat $p.diff + echo + ) >>apidiff/next.txt.new + fi + rm $p.diff +done +if test -s apidiff/next.txt.new; then ( - echo '#' $p - apidiff -incompatible tmp/apidiff/$prevversion/$p.api tmp/apidiff/next/$p.api + echo "Below are the incompatible changes between $prevversion and next, per package." echo - ) >>apidiff/next.txt -done + cat apidiff/next.txt.new + ) >apidiff/next.txt + rm apidiff/next.txt.new +else + mv apidiff/next.txt.new apidiff/next.txt +fi diff --git a/apidiff/next.txt b/apidiff/next.txt index e69de29bb..61204369d 100644 --- a/apidiff/next.txt +++ b/apidiff/next.txt @@ -0,0 +1,5 @@ +Below are the incompatible changes between v0.0.13 and next, per package. + +# webhook +- PartStructure: removed + diff --git a/queue/hook.go b/queue/hook.go index 700bec49b..94fb88d3e 100644 --- a/queue/hook.go +++ b/queue/hook.go @@ -3,6 +3,7 @@ package queue import ( "context" "encoding/json" + "errors" "fmt" "io" "log/slog" @@ -796,7 +797,7 @@ func Incoming(ctx context.Context, log mlog.Log, acc *store.Account, messageID s log.Debug("composing webhook for incoming message") - structure, err := webhook.PartStructure(log, &part) + structure, err := PartStructure(log, &part) if err != nil { return fmt.Errorf("parsing part structure: %v", err) } @@ -912,6 +913,38 @@ func Incoming(ctx context.Context, log mlog.Log, acc *store.Account, messageID s return nil } +// PartStructure returns a webhook.Structure for a parsed message part. +func PartStructure(log mlog.Log, p *message.Part) (webhook.Structure, error) { + parts := make([]webhook.Structure, len(p.Parts)) + for i := range p.Parts { + var err error + parts[i], err = PartStructure(log, &p.Parts[i]) + if err != nil && !errors.Is(err, message.ErrParamEncoding) { + return webhook.Structure{}, err + } + } + disp, filename, err := p.DispositionFilename() + if err != nil && errors.Is(err, message.ErrParamEncoding) { + log.Debugx("parsing disposition/filename", err) + } else if err != nil { + return webhook.Structure{}, err + } + s := webhook.Structure{ + ContentType: strings.ToLower(p.MediaType + "/" + p.MediaSubType), + ContentTypeParams: p.ContentTypeParams, + ContentID: p.ContentID, + ContentDisposition: strings.ToLower(disp), + Filename: filename, + DecodedSize: p.DecodedSize, + Parts: parts, + } + // Replace nil map with empty map, for easier to use JSON. + if s.ContentTypeParams == nil { + s.ContentTypeParams = map[string]string{} + } + return s, nil +} + func isAutomated(h textproto.MIMEHeader) bool { l := []string{"List-Id", "List-Unsubscribe", "List-Unsubscribe-Post", "Precedence"} for _, k := range l { diff --git a/queue/hook_test.go b/queue/hook_test.go index 75cebd1da..b1190c537 100644 --- a/queue/hook_test.go +++ b/queue/hook_test.go @@ -82,7 +82,7 @@ func TestHookIncoming(t *testing.T) { tcheck(t, err, "decode incoming webhook") in.Meta.Received = in.Meta.Received.Local() // For TZ UTC. - structure, err := webhook.PartStructure(pkglog, &part) + structure, err := PartStructure(pkglog, &part) tcheck(t, err, "part structure") expIncoming := webhook.Incoming{ diff --git a/webapisrv/server.go b/webapisrv/server.go index 8505ba9c5..8e5319835 100644 --- a/webapisrv/server.go +++ b/webapisrv/server.go @@ -44,7 +44,6 @@ import ( "github.com/mjl-/mox/store" "github.com/mjl-/mox/webapi" "github.com/mjl-/mox/webauth" - "github.com/mjl-/mox/webhook" "github.com/mjl-/mox/webops" ) @@ -1263,7 +1262,7 @@ func (s server) MessageGet(ctx context.Context, req webapi.MessageGetRequest) (r MailboxName: mb.Name, } - structure, err := webhook.PartStructure(log, &p) + structure, err := queue.PartStructure(log, &p) xcheckf(err, "parsing structure") result := webapi.MessageGetResult{ diff --git a/webapisrv/server_test.go b/webapisrv/server_test.go index 995accb32..3e507c513 100644 --- a/webapisrv/server_test.go +++ b/webapisrv/server_test.go @@ -25,7 +25,6 @@ import ( "github.com/mjl-/mox/queue" "github.com/mjl-/mox/store" "github.com/mjl-/mox/webapi" - "github.com/mjl-/mox/webhook" ) var ctxbg = context.Background() @@ -418,7 +417,7 @@ func TestServer(t *testing.T) { tcheckf(t, err, "reading raw message") part, err := message.EnsurePart(log.Logger, true, bytes.NewReader(b.Bytes()), int64(b.Len())) tcheckf(t, err, "parsing raw message") - structure, err := webhook.PartStructure(log, &part) + structure, err := queue.PartStructure(log, &part) tcheckf(t, err, "part structure") tcompare(t, structure, msgRes.Structure) diff --git a/webhook/webhook.go b/webhook/webhook.go index 9582c5543..bda6249f6 100644 --- a/webhook/webhook.go +++ b/webhook/webhook.go @@ -8,12 +8,7 @@ package webhook import ( - "errors" - "strings" "time" - - "github.com/mjl-/mox/message" - "github.com/mjl-/mox/mlog" ) // OutgoingEvent is an activity for an outgoing delivery. Either generated by the @@ -145,35 +140,3 @@ type Structure struct { DecodedSize int64 // Size of content after decoding content-transfer-encoding. For text and HTML parts, this can be larger than the data returned since this size includes \r\n line endings. Parts []Structure // Subparts of a multipart message, possibly recursive. } - -// PartStructure returns a Structure for a parsed message part. -func PartStructure(log mlog.Log, p *message.Part) (Structure, error) { - parts := make([]Structure, len(p.Parts)) - for i := range p.Parts { - var err error - parts[i], err = PartStructure(log, &p.Parts[i]) - if err != nil && !errors.Is(err, message.ErrParamEncoding) { - return Structure{}, err - } - } - disp, filename, err := p.DispositionFilename() - if err != nil && errors.Is(err, message.ErrParamEncoding) { - log.Debugx("parsing disposition/filename", err) - } else if err != nil { - return Structure{}, err - } - s := Structure{ - ContentType: strings.ToLower(p.MediaType + "/" + p.MediaSubType), - ContentTypeParams: p.ContentTypeParams, - ContentID: p.ContentID, - ContentDisposition: strings.ToLower(disp), - Filename: filename, - DecodedSize: p.DecodedSize, - Parts: parts, - } - // Replace nil map with empty map, for easier to use JSON. - if s.ContentTypeParams == nil { - s.ContentTypeParams = map[string]string{} - } - return s, nil -}